Trying Out Remixtrying-out
I came across Supporting Remix with full stack Cloudflare Pages on Cloudflare blog (yes I’m a CF fan) and Remix piqued my interest. Mostly because it addresses my concerns with “modern” web development: the wiring boilerplate is mostly gone, there is no 100 file setup to get a workable dev environment. And of course because it’s outside my comfort zone - so maybe I’ll learn something.
There’s a code example right on the home page but I’m having hard time building a mental model from just that. Luckily there is also a bit Get started button which takes me to a tutorial. Great, let’s follow that.
Few more commands
And we’re up.
Then there is text that really speaks to me
If you want, take a minute and poke around the starter template, there’s a lot of information in there.
Oh yes I intend to do that 😄
Let’s make a git repo to keep track of changes. Conveniently there is aready a
Let’s now see what we have
Honestly less than I expected. And I mean this in the best possible way. I like
environments which I can understand to the point of being able to write manually
from scratch. Yest I’m looking at you
create-react-app and other humongous
frontend toolchains. I feel at home with Go where you have your dependencies
file and a single
Back to source files; there’s a typescript config but no webpack config (yay?) or similar. This is in the typescript config
Great! Let’s find the entrypoint now to better understand this.
Since I’ve been instructed to use
npm run dev this means that
will define the action.
And it does, no magic here.
There is something the looks like remix configuration (
And apparently it points to
app directory. Conveniently there I can find
entry.server.tsx which I guess are my entrypoints. What
I found slightly strange is
.tsx (which I believe is JSX for typescript) for
Maybe time to read some more docs 🤷
Baby’s first code
Confused for a moment…apparently docs are not up to date, the links are now in
app/routes/index.tsx. Oh well, I’ll manage. I added the
li as instructed
and then of course it fails to compile as
Link is not defined. Guessed the
import based on imports in
root.tsx to be
And non-surprisingly it shows up now. Time for some remix magic now.
If your web dev background is primarily in the last few years, you’re probably used to creating two things here: an API route to provide data and a frontend component that consumes it. In Remix your frontend component is also its own API route and it already knows how to talk to itself on the server from the browser. That is, you don’t have to fetch it.
And it show up as expected.
Then the tutorial moves on to refactoring, extracting data and fetching from an external data source but I’m more interested in this mechanics as it seems to me this is the meat and the potatoes of remix. So what is going on?
Well apparently the
useLoaderData hook is automatically tied to corresponding
loader in the same route. By convention instead of us manually wiring
up routing, neat. But I have questions now 😄 Can I pass parameters? Is
useQuery-like magical caching and deduplication? Authentication? CORS?
More interestingly: if I looks at network in devtools I don’t see an XHR
request, meaning our data is pre-rendered on the server. Super neat! But if i
navigate from index to posts I see a request to
http://localhost:3000/posts?_data=routes%2Fposts%2Findex which directly
returns my data.
the tutorial actually explains how to parametrize aka how to do
dynamic route params.
It’s again done by convention, not explicit config: file name is a placeholder for a parameter. Clever?
I’ll admit my knee jerk reaction to this is “ugh, ugly. I want my router”. But it may work? I don’t know, would actually need to do a larger project to evaluate properly. In any case there is a way to get an overview of the routes:
So I can still have my cake (easily overview and discover routes) and eat it (not write the router) too.
Ok, so how does this paramterized component look like?
And sure enough I can now click on one of the posts and it renders
So what is happening here?
- My url path
/posts/my-first-postis matched against the
posts/:slugroute and my component is rendered
- then the
useLoaderDatahook fires and data for self url is requested - more concretely when navigating on frontend a request to
posts/my-first-post?_data=routes%2Fposts%2F%24slugis dispatched (I’m assuming this is bypassed when server-side rendered and loader is called directly)
- server side matches this against routes again, picks up my page but since
we’re requesting data it now calls the
loaderwith query path parameters passed in as named values in
params. The basic example is just returning a string here but it could be any json, probably with data from some external data source as well
- response from the loader is wired (via fetch/XHR or server side rendering) to
the hook and the string returned from the loader is bound to
Now the analogy from the tutorial that loader is the controller and we’re using react as a view layer makes sense 😀
But meanwhile I started wondering…can I put multiple components on screen and each will automagically fetch it’s data? I could peruse the docs some more…or I can just try it out.
I created this
Yes, very silly, calling a server to get current year, but I just want to test things out 😅
One remark while I figure things out: tooling is quite responsive and helpful: change detection, auto-recompilation and reloading out of the box.
And a cryptic error appears
What is slug doing here? Sprinkling in some logging I notice that
actually holds data from
$slug.txs loader, not my footer loader. So I’m
holding it wrong - back to the docs it is.
Next interesting bit is that components and also include styles
and this gets picked up by
Links component in
But the real fun begins with
index routes. By adding this to
things can now render inside this component. E.g. visiting
app/routes/admin.tsx but inside the
Outlet there will be
/app/routes/admin/new.tsx. This time with working data fetching 😉 But
does this mean I can only structure my data-fetching components hierarchically?
Reading on I’m slightly surprised to see that remix includes its own forms. How
do they differ from plain old html
<form>? I’m guessing some automagical
wiring to the backend, maybe shared validation logic 🤞
And sure enough, there is wiring by convention
So what happens when I submit this form? Feels very smooth, let’s look under the cover.
POSTrequest is made to
/admin/new?_data=routes%2Fadmin%2Fnewwith regular form data payload.
- but the response is interesting: status is set to
204 No Contentand there is an interesting header:
- apparently the “redirect” is then actually just frontend navigation to new
route as there are no more requests except one for new data
How about deployment? I did start this with the intent of deploying to Cloudflare but the post is already getting long and the app as-is is not really suitable (direct filesystem access is not supported) for deploying, so maybe in another post.
Now, I would probably not pick this stack for a new project, mostly to me not being comfortable with Node, but I might just use it for an opinionated React toolchain with SSR and all other goodies.
Last modified on 2021-12-19Previous Running amd64 docker images with Podman on Apple Silicon (M1)
Next Speed is a feature; thoughts on modern web performance