A First Look at Remix

ajcwebdev - May 7 '21 - - Dev Community

Outline

All of this project's code can be found in the First Look monorepo on my GitHub.

Introduction

Remix is a React metaframework from Ryan Florence and Michael Jackson that builds on standard web APIs. At the time of the framework's release, they were already well known as the maintainers of React Router and educators behind React Training. But in the beginning of 2020, widespread quarantine measures were enacted across the country.

Quarantine effectively brought an end to all in-person technical training. Once the impeding demise of their livelihood became apparent, the two founders helped their remaining employees find new remote roles before they inevitable were forced to lay them off. About a month later in April 2020, they announced Remix.

Originally requiring a paid license, it launched as a "supporter preview" in October 2020. A little over a year later, in November 2021, it was fully open-sourced. This was accompanied by the founders starting a company, raising a $3 million seed round, and hiring notable React developer and teacher, Kent C. Dodds.

Kent had become infatuated with Remix and rebuilt his entire platform around the framework. During the rebuild he also attempted to individually tell every single person on the internet why using anything except Remix to build literally anything was a terrible choice. He was soon hired as a Developer Advocate and early co-founder of the newly formed company. In this example we will use the Vercel starter to make Fetch requests containing GraphQL queries.

Initialize Starter Project

The create-remix command can be used to generate various Remix templates with varying deployment providers. We'll specify the vercel template and select JavaScript when prompted.

npx create-remix --template vercel ajcwebdev-remix
Enter fullscreen mode Exit fullscreen mode

Make sure to select No for npm install or it will fail due to an unresolvable peer dependency.

? Do you want me to run `npm install`? No
? TypeScript or JavaScript? JavaScript

đź’ż That's it! `cd` into "ajcwebdev-remix" and check
the README for development and deploy instructions!
Enter fullscreen mode Exit fullscreen mode

Start Development Server

cd into your project, install the Vercel CLI, and start the development server.

cd ajcwebdev-remix
yarn add -D vercel
echo > app/routes/graphql.jsx
yarn dev
Enter fullscreen mode Exit fullscreen mode
Watching Remix app in development mode...
đź’ż Built in 161ms

Remix App Server started at http://localhost:3000 (http://192.168.1.78:3000)
Enter fullscreen mode Exit fullscreen mode

Open localhost:3000 to see the project.

01-home-page-on-localhost-3000

Index Routes

index routes are routes that renders when the layout's path is matched exactly. If you have an index.jsx file in the routes directory it will be used as the home page. I've made a few edits to the boilerplate code.

// app/routes/index.jsx

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

export let loader = () => {
  let data = {
    resources: [
      { name: "My Blog", url: "https://ajcwebdev.com" },
      { name: "A First Look at Remix", url: "https://ajcwebdev.com/a-first-look-at-remix" },
      { name: "Example Repo", url: "https://github.com/ajcwebdev/ajcwebdev-remix" }
    ]
  }
  return json(data)
}

export let meta = () => {
  return {
    title: "ajcwebdev-remix", description: "Welcome to Remix!"
  }
}

export default function Index() {
  let data = useLoaderData()

  return (
    <div className="remix__page">
      <main>
        <h1>ajcwebdev-remix</h1>
        <p>Woot!</p>
      </main>

      <section>        
        <h2>Resources</h2>
        <ul>
          {data.resources.map(resource => (
            <li key={resource.url}>
              <a href={resource.url}>{resource.name}</a>
            </li>
          ))}
        </ul>
      </section>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

json provides a shortcut for creating application/json responses and meta sets meta tags for the HTML document.

02-home-page-on-localhost-3000-edit

CSS Styling

Include Water CSS for some styling presets.

// app/root.jsx

import {
  Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration
} from "@remix-run/react"

export const links = () => {
  return [{
    rel: "stylesheet",
    href: "https://cdn.jsdelivr.net/npm/water.css@2/out/dark.css"
  }]
}

export const meta = () => ({
  charset: "utf-8",
  title: "A First Look at Remix with GraphQL",
  viewport: "width=device-width,initial-scale=1",
})

export default function App() {
  return (
    <html lang="en">
      <head>
        <Meta />
        <Links />
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  )
}
Enter fullscreen mode Exit fullscreen mode

03-home-page-with-water-css

Loader Functions

Loaders provide data to components and are only ever called on the server. Since the function only runs on the server, it's an ideal candidate for requests that include API secrets that cannot be exposed on the client. At the beginning of the tutorial, we created a new route for a GraphQL query called graphql.jsx.

// app/routes/graphql.jsx

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

export let loader = async () => {
  // fetch request
}

export default function Index() {
  let { data } = useLoaderData()
  console.log(data)

  return (
    <></>
  )
}
Enter fullscreen mode Exit fullscreen mode

Each route can define a "loader" function that will be called on the server before rendering to provide data to the route. You can connect to a database or run any server side code next to the component that renders it.

// app/routes/graphql.jsx

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

const GET_CHARACTERS = `{
  characters {
    results {
      name
      id
    }
  }
}`

export let loader = async () => {
  const res = await fetch(
    'https://rickandmortyapi.com/graphql', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        query: GET_CHARACTERS
      })
    }
  )
  const characters = await res.json()
  return characters
}

export default function Index() {
  let { data } = useLoaderData()

  return (
    <>
      <ul>
        {data.characters.results.map(({ name, id }) => (
          <li key={id}>
            {name}
          </li>
        ))}
      </ul>
    </>
  )
}
Enter fullscreen mode Exit fullscreen mode

04-graphql-route-with-characters-data

Deploy to Vercel

The starter already includes Vercel specific configuration in remix.config.js and the @remix-run/vercel package in server.js. You can deploy your app by importing a Git repository into Vercel. If you'd like to avoid using a Git repository, you can directly deploy the project with the Vercel CLI:

yarn vercel
Enter fullscreen mode Exit fullscreen mode

If this is your first time using the Vercel CLI, it will first ask you to login. You will then be asked a series of questions to configure your Vercel deployment.

? Set up and deploy “~/ajcwebdev-remix”? [Y/n] y
? Which scope do you want to deploy to? Anthony Campolo
? Link to existing project? [y/N] n
? What’s your project’s name? ajcwebdev-remix
? In which directory is your code located? ./

Auto-detected Project Settings (Remix):
- Build Command: remix build
- Output Directory: public
- Development Command: remix dev
? Want to override the settings? [y/N] n

đź”—  Linked to ajcwebdev/ajcwebdev-remix (created .vercel)
🔍  Inspect: https://vercel.com/ajcwebdev/ajcwebdev-remix/9pRFBDM3BgCnMEHdt2CctPMz6V7c [2s]
âś…  Production: https://ajcwebdev-remix.vercel.app [copied to clipboard] [1m]
đź“ť  Deployed to production. Run `vercel --prod` to overwrite later (https://vercel.link/2F).
đź’ˇ  To change the domain or build command, go to https://vercel.com/ajcwebdev/ajcwebdev-remix/settings
Enter fullscreen mode Exit fullscreen mode

Open your website URL to see the deployed project.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .