How Remix gets the best of all worlds

SPA, SSR, SSG

6 Jul 2022

Adrian Fâciu

SPA, SSR, SSG

SPA

React

Vue

Angular

Backbone

Ember

Svelte

SPA

Source: https://www.blog.duomly.com/client-side-rendering-vs-server-side-rendering-vs-prerendering/

SSG

Gatsby

Hugo

Jekyll

Astro

Hexo

SSG

Source: https://www.cosmicjs.com/blog/static-site-generators-explained-in-5-minutes

SSR

React (Server Components)

SvelteKit

NextJs

Nuxt

Angular Universal

SSR

Source: https://www.blog.duomly.com/client-side-rendering-vs-server-side-rendering-vs-prerendering/

(Re)hydration

Source: https://web.dev/rendering-on-the-web/

Benefits

  • SPAs have better UX

  • SSR apps have better SEO

  • SSG don't need a server

Benefits

  • SPAs load faster (after the initial load)

  • SSR first load is usually fast

  • SSG are constantly fast

Mixing them together

What is Remix

Compiler

Server framework

Server side HTTP handler

Client framework

import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";

export const loader = async () => {
  return json({
    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();
  console.log(posts);
  return (
    <main>
      <h1>Posts</h1>
    </main>
  );
}
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";

export const loader = async () => {
  return json(db.getPosts());
};

export default function Posts() {
  const { posts } = useLoaderData();

  return (
    <main>
      <h1>Posts</h1>
      <ul>
        {posts.map(post => (<li>{post.title}</li>)}
      </ul>
    </main>
  );
}

Server side HTTP handler

  • not really a server

  • built on web fetch API

  • runs on any JavaScript server

Can be deployed on

  • Vercel

  • Netlify

  • AWS Architect

Can be deployed on

  • CloudFlare workers/pages

  • Deno deploy

  • Netlify Edge functions

Remix

Features

Nested routes

Nested routes

import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";

export const loader = async () => {
  return json(db.getPosts());
};

export default function Posts() {
  const { posts } = useLoaderData();

  return (
    <main>
      <h1>Posts</h1>
      <ul>
        {posts.map(post => (<li>{post.title}</li>)}
      <ul>
    </main>
  );
}

Nested routes

Nested routes

Nested routes

Nested routes

Nested routes

Most web apps fetch inside of components, creating request waterfalls, slower loads, and jank.

Nested routes

Nested routes

Data updates

Data updates

export default function NewInvoice() {
  return (
    <Form method="post">
      <input type="text" name="company" />
      <input type="text" name="amount" />
      <button type="submit">Create</button>
    </Form>
  );
}

export async function action({ request }) {
  const body = await request.formData();
  const invoice = await createInvoice(body);
  return redirect(`/invoices/${invoice.id}`);
}

Data updates

  • revalidates data client side

  • handles race conditions

  • provides transitions

Pending & Optimistic UIs

Pending UIs

export default function NewInvoice() {
  const transition = useTransition();
  return (
    <Form method="post">
      <input type="text" name="company" />
      <input type="text" name="amount" />
      <button type="submit">
        {transition.state === "submitting"
          ? "Creating invoice..."
          : "Create invoice"}
      </button>
    </Form>
  );
}

Optimistic UIs

export default function NewInvoice() {
  const { submission } = useTransition();
  return submission ? (
    <Invoice
      invoice={Object.fromEntries(
        submission.formData
      )}
    />
  ) : (
    <Form method="post">
      <input type="text" name="company" />
      <input type="text" name="amount" />
      <button type="submit">
        Create invoice
      </button>
}

Error boundaries

Error boundaries

export default function InvoiceRoute() {
  const invoice = useLoaderData();
  return <Invoice data={invoice} />;
}

export function ErrorBoundary({ error }) {
  console.error(error);
  return (
    <div>
      <h2>Oh snap!</h2>
      <p>
        There was a problem loading this invoice
      </p>
    </div>)
 }

Styling

Styling

import { Links } from "@remix-run/react";

export function links() {
  return [
    {
      rel: "stylesheet",
      href: "https://unpkg.com/modern-css-reset@1.4.0/dist/reset.min.css",
    },
  ];
}

export default function Root() {
  return (
    <html>
      <head>
        <Links />
        {/* ... */}
      </head>
      {/* ... */}
    </html>
  );
}

Remix stacks

Indie

Blues

Grunge

+

Custom

 

stack

Recap

Remix vs Next?

How about
React?!?

Resources

Q & A

Q & A

Share your experience or impression

Remix

By Adrian Faciu

Remix

  • 418