Another day, another front-end framework!
Remix takes advantage of the server client model; with many things done on the server side it makes applications extremely fast.
Combines HTTP caching, URL assets and dynamic server rendering.
When you spin up a simple remix app, you’ll see two folders, app
and public
.
Within app
there is a route
folder. This acts like an index file on the application.
You can add folders such as styles
within the app folder, as well as others such as config
.
The public
folder, like other web applications, is used to store static files such as fav icons.
When creating routes, it’s as simple as creating a folder under routes and then adding an index file.
For example:
app/routes/new-page/index.tsx
export default function NewPage() {
return (
<main>
<p>My new page</p>
</main>
)
}
In Remix, you can create an API route
, this provides data and a front-end component that consumes it.
Remix includes something called loaders
, these only run on the server and are the backend “API” for the component. It’s used within components via useLoaderData
.
import { useLoaderData } from 'remix'
export const loader = async () => {
return ['one', 'two']
}
export default function Example() {
const data = useLoaderData()
return(
<div>
<h1>Some data</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
)
}
Here’s a complete example, taken from the docs:
// app/routes/posts/index.tsx
import { json } from "@remix-run/node";
import { Link, useLoaderData } from "@remix-run/react";
type Post = {
slug: string;
title: string;
};
type LoaderData = {
posts: Array<Post>;
};
export const loader = async () => {
return json<LoaderData>({
posts: [
{
slug: "my-first-post",
title: "My First Post",
},
{
slug: "90s-mixtape",
title: "A Mixtape I Made Just For You",
},
],
});
};
export default function Posts() {
const { posts } = useLoaderData() as LoaderData;
return (
<main>
<h1>Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.slug}>
<Link
to={post.slug}
className="text-blue-600 underline"
>
{post.title}
</Link>
</li>
))}
</ul>
</main>
);
}
The above example can be refactored. It is better to create modules that deal with reading and writing posts. To do this to the example above, you can set up a getPosts
export.
// app/models/post.server.ts
type Post = {
slug: string;
title: string;
};
export async function getPosts(): Promise<Array<Post>> {
return [
{
slug: "my-first-post",
title: "My First Post",
},
{
slug: "90s-mixtape",
title: "A Mixtape I Made Just For You",
},
];
}
The post route can be updated as follows:
import { json } from "@remix-run/node";
import { Link, useLoaderData } from "@remix-run/react";
import { getPosts } from "~/models/post.server";
type LoaderData = {
// this is a handy way to say: "posts is whatever type getPosts resolves to"
posts: Awaited<ReturnType<typeof getPosts>>;
};
export const loader = async () => {
return json<LoaderData>({
posts: await getPosts(),
});
};