After I wrote Moving Away from Vercel and NextJS, I converted a couple of projects over to AstroJS. I do not regret it at all, Astro's simplicity made it very easy to move over my projects and get deployed to CloudFlare Pages with 0 effort.
But there's another project I've been struggling with, Introspect. My home for my recommended courses.I'm set on taking full advantage of modern JS frameworks for Introspect. But the problem is we're in the early stages of changing times. It's something I hinted at but didn't fully understand in React Development is about to Change.A move to make static websites as dynamic as single-page apps. I think it's very interesting that a lot of frameworks seem to be converging on this idea. But the question is, who is doing it best?
I've been exploring different JS frameworks and I've started to develop a few requirements that I need met.
Let's talk about the options.
Abstracts Browser APIs into HTML.
Needs framework for work with edge such as creating Buffer's for PassKey Auth.
No form action interception, defaults to AJAX APIs.
Astro: This is of course what I landed on originally. And it works great for my blog and CMS backed apps. Linting it isn't great. Even with modifying my own config, dealing with .astro files have been a small, but acceptable headache. Because Astro just builds to static files, you can do the same things with it that you can with HTMX. Except you can use JSX and TypeScript to achieve it. Which means much faster development. Also, with Astro you have to choose between static HTML forms (with full page reloads) or using a UI library for AJAX submissions + data invalidation. For an app like introspect, this means writing JSON API routes for a lot of interactions (including many future planned features with a lot more user interactions). Astro is a little weak with dashboard type apps, but to be fair, so was SPA. SPA only made them viable/realistic.
Frontmatter allows you to write server side logic for form actions.
Exposes plain Request/Response objects to use native browser APIs.
Excellent CloudFlare adapter that automatically adds NodeJS compatibility. Deploying to pages doesn't feel like you're deploying to a different environment. Even environment variables are available on the newer import(dot)meta(dot)env. (process does not exist on edge runtime).
Astro does not have a client side router that allows form actions to invalidate data and refresh that data without full page reloads.
NextJS: The framework that swears it's stable, even if the entire world is disagreeing. NextJS released an incomplete version of what I've been looking for and it's the reason I'm looking for an alternative. Before App Router, people used NextJS JSON routes and tRPC to write endpoints for their server logic and called them with TanStack Query which handled client side data invalidation. It's the writing endpoints I'd like to get away from. A GET request to a route should return HTML, and a POST request should run some logic and invalidate the content/cache of the GET request. That is the basic idea Next has been trying to move to, but by choosing to go server-side cache instead of client-side cache they've created some difficult to work with, and very broken APIs that depend on Vercel infrastructure. And to achieve this, they are exposing experimental imports from the React library for tracking form status, and using RSC which is canary in React. But according them... it's “stable”.
This gets a no because this is not currently possible without using experimental React imports. Anyone using stable APIs must depend on JSON API routes and TanStack Query the same way we did before app router.
Use of browser APIs is severly limited because of the Vercel infrastructure strategy, server-side caching, and HTML streaming. Developers have lost access to simple things like headers and cookies which makes it difficult to work with Browser APIs.
NextJS was built with edge runtime in mind. However basic things like the Image component do not work outside of Vercel. Every provider has to go through great pains to support NextJS without guidance on how do so. To get images to display with CloudFlare, they have a guide on creating a custom loader for images on CloudFlare CDN. But that is not a complete solution.
Same as the second point, in terms of intercepting HTML form actions, these features are experimental.
Remix: This framework has always felt like a response to NextJS. Always comparing themselves to Next, always proclaiming that their way is the right way of doing things. And granted, in terms of how things are evolving right now, Remix is in a great position. I rebuilt parts of Introspect with Remix and really like the way they handle forms. Most of it's core features are specifically what I'm looking for.
Remix was basically made to couple React Router, server side logic, and HTML forms.
Does a great job of exposing Browser APIs where they're used and encouraging their use as much as possible.
The CloudFlare adapter is a bit rough. You have to get environment variables off of context in loaders/actions, which isn't the end of the world, but can be annoying. And NodeJS compatibility is not available.
By default Remix encourages using React Router + HTML forms for data invalidation, which is amazing. But it also provides a ‘useSubmit’ hook that can be good for running client side logic before posting a form while still keeping the invalidation features.
QwikCity doesn't really have much of a client-side anything. It uses a very raw and incomplete implementation of signals to synchronize state which causes the server to often be one step behind the client. And form values are not pulled from input values. Put these together and forms are unreliable.
The CloudFlare adapter for QwikCity works great as long as you use the CloudFlare template. However, the issue I have here is Qwik serialization. If you care about NodeJS compatibility, you're thinking about things like Buffer and Crypto. Because these can't be serialized, it's often difficult to figure out how to get them through Qwik's numerous “serialization boundaries”.