Introduction to Remix
With this post I want to help you get started with Remix with my usual 80/20 approach: skip the fluff, learn the core®
What’s Remix? It’s a React-based framework.
Do you know Next.js? Or SvelteKit? Well, Remix is something like that, but with some unique features that make it an interesting alternative. But it has no static site support, so it always needs a server.
Which makes it good for some use cases, bad for others.
Good for use cases where you have a database, dynamic data, user accounts with private data, and so on. Like with a Rails, Django or Laravel app.
We create a new Remix project using npx
, so we don’t have to install anything or even create a folder up front.
Just to in the folder that contains your projects (it’s usually dev
or www
for me) and type
npx create-remix@latest
Choose the folder you want to install to:
Pick the Remix App Server option, which is the built-in server (you can change that later):
Pick JavaScript, or TypeScript if you prefer:
Make sure you tell the installer to run npm install
so that it can set up everything for you:
Then cd <foldername>
and run npm run dev
.
It’s always nice to look at the result of the default installation:
The code that generates this is this:
But we’re only actually interested in the app
folder, the rest is boilerplate/configuration:
Let’s analyze that.
We have 3 files in the src
folder:
entry.client.jsx
entry.server.jsx
root.jsx
Which are used to set the overall site functionality, including the HTML output for all pages. They’re the ones that if you touch them, the entire app will be affected. And that’s where error handling happens, which is a first-class aspect in Remix, which is kind of cool.
Then we have a routes
folder with an index.jsx
file, and a demos
folder which contains a few demo applications.
Let’s start with routes/index.jsx
.
It’s a long React component:
import { useLoaderData, json, Link } from "remix";
// Loaders provide data to components and are only ever called on the server, so
// you can connect to a database or run any server side code you want right next
// to the component that renders it.
// https://remix.run/api/conventions#loader
export let loader = () => {
let data = {
resources: [
{
name: "Remix Docs",
url: "https://remix.run/docs"
},
{
name: "React Router Docs",
url: "https://reactrouter.com/docs"
},
{
name: "Remix Discord",
url: "https://discord.gg/VBePs6d"
}
],
demos: [
{
to: "demos/actions",
name: "Actions"
},
{
to: "demos/about",
name: "Nested Routes, CSS loading/unloading"
},
{
to: "demos/params",
name: "URL Params and Error Boundaries"
}
]
};
// https://remix.run/api/remix#json
return json(data);
};
// https://remix.run/api/conventions#meta
export let meta = () => {
return {
title: "Remix Starter",
description: "Welcome to remix!"
};
};
// https://remix.run/guides/routing#index-routes
export default function Index() {
let data = useLoaderData();
return (
<div className="remix__page">
<main>
<h2>Welcome to Remix!</h2>
<p>We're stoked that you're here. 🥳</p>
<p>
Feel free to take a look around the code to see how Remix does things,
it might be a bit different than what you’re used to. When you're
ready to dive deeper, we've got plenty of resources to get you
up-and-running quickly.
</p>
<p>
Check out all the demos in this starter, and then just delete the{" "}
<code>app/routes/demos</code> and <code>app/styles/demos</code>{" "}
folders when you're ready to turn this into your next project.
</p>
</main>
<aside>
<h2>Demos In This App</h2>
<ul>
{data.demos.map(demo => (
<li key={demo.to} className="remix__page__resource">
<Link to={demo.to} prefetch="intent">
{demo.name}
</Link>
</li>
))}
</ul>
<h2>Resources</h2>
<ul>
{data.resources.map(resource => (
<li key={resource.url} className="remix__page__resource">
<a href={resource.url}>{resource.name}</a>
</li>
))}
</ul>
</aside>
</div>
);
}
Now, this file could be simplified down to a super simple React component like this:
export default function() {
return (
<div className="remix__page">
Test
</div>
);
}
and if you do so and save the file, you’d see this in the app:
But this file is doing something more than just outputting some JSX. It’s loading data on the server-side, via the loader()
function:
import { json } from "remix";
export let loader = () => {
let data = {
//.... some data
};
return json(data);
};
json()
is a special utility to easily create a JSON endpoint that Remix will then call when the page loads, to fill it with the data it needs.
This loader()
function is called before rendering, and only runs server-side.
To use this data in the component’s JSX, we must import and call useLoaderData()
:
import { useLoaderData, json } from "remix"
export let loader = () => json({
name: 'Flavio'
})
export default function() {
let data = useLoaderData()
return (
<div className="remix__page">
Hi {data.name}
</div>
)
}
The file also has a meta()
function exported, which is used to set the page HTML head’s meta data.
Routes are where the “meat” happens in Remix. Which is understandable since it was created by the people that made React Router, so the router is the center part of the app.
The official tutorial already talks about creating custom routes and handling forms, so I won’t go in more details about that now.
I suggest you to take a look at that, especially at forms.
Why? Because forms are a big pain in React, so it’s nice to see this very simple approach at allowing you to create forms without having to write lots and lots of boilerplate code.
I think that’s the nicest part of Remix and I’d consider using this if I want to have a site with first-class forms, to avoid using libraries that I need to learn from scratch and are another dependency to worry about.
Another interesting thing is child routes, and how that basically replicates what we used to do with Ember and its outlets in the pre-React days, and it makes sense since I remember Ryan Florence being active in the Ember community back in the day.
And it’s great to see progress in a field that has not seen any big incumbents, where Next.js is basically the elephant in the room and it’s the thing I’ve defaulted to so far when it comes to writing an app.
And I’ll still default to that for the time being. I like new tech, but it also takes a long time for it to become mature, have people write libraries, tutorials, etc etc.
But competition fosters innovation, so it’s always good to have an option that is peculiar enough to not be a copycat of existing alternatives.
→ I wrote 17 books to help you become a better developer, download them all at $0 cost by joining my newsletter
→ JOIN MY CODING BOOTCAMP, an amazing cohort course that will be a huge step up in your coding career - covering React, Next.js - next edition February 2025