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(),
});
};