DEV Community

Cover image for Ready for a "Remix" on React?
Raymond Luu for Developers @ Asurion

Posted on

Ready for a "Remix" on React?

You might be curious about the title and think that this blog post is about React… well sort of. This will be about a new framework I am learning called Remix, which under the hood includes React. Check it out here

I have been working on React for the past 4 years and lately I have been hearing a lot about these newer frameworks like Next.js and Remix. It got me thinking about what these frameworks might offer, plus who wouldn’t want to learn new things in the React ecosystem? I certainly would like to!

My goal here is not to repeat the quick start guide that they provide but to highlight some of the cool things that I learned from following it (they did a great job in guiding readers through the cool features that Remix has). Getting started was super easy! I think it literally took me 5-10mins just to get things running and start coding! It is crazy how fast it is these days. Here is a link to the video tutorial if you prefer that instead w/ Instructor Kent C. Dodds!

I have done a small bit of diving into Next.js but this isn't really about a comparison between the two. It is mainly to give you an introduction into what Remix is from what I have learned! Enough with the intro and let’s dive right in and talk about all the cool things that Remix offers.

What I liked about Remix

  • Easy setup!
  • Full stack!
  • Remix has great documentation
  • Routing is built into project structure
  • The code to write for loading data is simple
  • Creating forms with Remix Actions looks clean

I will break down some of these bullet points down below.

Let's start remixing

Getting started is super quick and easy!

There are some prerequisites so make sure you are up to date on some of the versions for things like Node and NPM.

npx create-remix --template remix-run/indie-stack blog-tutorial
Enter fullscreen mode Exit fullscreen mode

The above command will get you to set up with their template and it includes a bunch of code to get you started. You can also follow along with their quick start guide; however, if you want to have a clean and minimal project with none of that just run this (replacing my-project-name w/ the name you desire):

npx create-remix my-project-name
Enter fullscreen mode Exit fullscreen mode

Then to start the dev server you just run the following:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Easy as that. Now you are ready to code! I do recommend starting with the template though. I did that through my first attempt to better understand the concepts and went back again working through a minimal project to play around with features that I was curious about outside of their blogging tutorial.

Frontend and Backend?

React is all frontend JavaScript. The server serves a single index.html file with all the JavaScript for your application bundled together. All functionality exists in the JavaScript code in the browser.

However, Remix is different. Remix is a full stack framework. Node.js on the backend and React on the frontend. Most of the components you will build are compiled on the server then served to the client and rendered onto the screen. Minimal JavaScript is sent to the client unless you want to include a few React functionalities, which we will talk about later.

The idea of being full stack really reminds me of the monolithic architecture that I was so used to learning back when I first started my software development career (for about a few months until React skyrocketed into popularity). I was specifically working with a LAMP stack back then. PHP for the server-side code that served all the pages along with any HTML/CSS/JavaScript that was needed for that page of the app. There was no API either. If the UI needed some data from the DB, it would call your PHP server code to fetch the data and serve the page with it.

Another stack I worked with was the MEAN stack. Remix to me is an evolved form of that. With the MEAN stack you had your Angular application served from one server while your Node.js was on a separate server acting as your API and bridge to your DB. That was full stack JavaScript but with JSON communication between two servers (one for UI and the other for API). What Remix does is takes us back to that monolithic architecture. One server to serve your UI to the client while providing it any necessary data from the DB.

Both MEAN stack and Remix are full stack JavaScript. Only difference is MEAN stack has two servers with JSON data layer for communication while Remix uses a single server for both the frontend and backend. This makes it feel like the lines get blurred between client and server if you worked on stacks like MEAN. However, it’s like the LAMP stack where all your code lives on the server and that serves the client what it needs to see depending on what part of the application you are using. Remix is very similar to Next.js in that sense as well! If you are interested in the maintainer's take on the differences check out this article! Now let’s look at how data loading works.

Loading data with Remix

Data loading is built into the Remix framework! Let's look at how that works.

dashboard-loader

In the above image we are mocking some data.

First off, I did make a comment in my code that we could move some of the data handling to a separate file. Separation of concerns here is great but it is included in this file to easily see everything in one place.

Remix will understand this loader function as a way for the UI to load the data. "Loaders" are the auto wired backend "API" connection for the components. In this case we just provide a mock object of an array of books with “id” and “title”. Typically, in a real-life scenario we would make an API call here to get our data or in Remix's case we call the DB layer to fetch the data we need for this view.

dashboard-render

This above code shows the main dashboard component.

Line 34 is where you will see the usage of that data from our loader above, then we return the HTML that includes the data we used to generate a list of that data. The “Links” are for routing which we will talk about later.

dashboard-imports

The above picture shows the imports I used.

For data loading we care about “json” from “@remix-run/node” and “useLoaderData” from “@remix-run/react”. The imports really help differentiate which side of the stack they are from. Node meaning it is from backend and React meaning it is from frontend.

dashboard-component

Here is a look at the full code for the Dashboard component I built above.

Does that not look simply easy? Obviously one thing to note is that we would want to replace the mock json with actual data from a DB. The tutorial shows you how if you are interested. They use Prisma as their ORM of choice. Let's move onto routing!

Routing

For me personally, the routing took a bit of time to understand and to get used to. When I initially learned Next.js I saw this similar pattern where the folder structure dictates your routing. The folder structure is a very nice feature because you can just look at it and see which component is rendered at which route. With React Router you had a separate file defining those routes and components that rendered at each. You don't have to worry about that single definition file here though.

routing-example-one

For an example of routing the above index.tsx file renders your component for the root of your app (localhost:3000/).

routing-example-two

In this example we see the file /routes/dashboard/index.tsx which will render your dashboard component at localhost:3000/dashboard

routing-example-three

For an example of a route slug, you will notice in the image above the file named "$slug.tsx"

This is considered a route param. To navigate to the component being rendered at this route we use “localhost:3000/book/1”. Then in your code you will have access to the property “slug” which is assigned the value 1 based on the “/1” part of the route. You don't have to be restricted to using “slug” it can be anything you choose. You could in fact rename the file to “$bookId.tsx” and in your code you will have access to “bookId” and the value 1 assigned to it.

The below image shows what the component looks like. You can see it uses the loader function and has params as an input which we return it in a JSON format. Then in the component we can access it with “useLoaderData”.

routing-example-three-code

You can get even fancier with Outlets. Remix provides a way for you to render different child routes. For example, if you wanted to do something like “/dashboard/admin” and “/dashboard/” to have different components for normal users vs admin users you can! The component being rendered at this route “/dashboard/” can include an “Outlet” which by default renders your dashboard for normal users. It can as well render your admin component at “/dashboard/admin”.

Here is the add-comment component example:

routing-outlets-example

File structure for "add-comment" component using Outlet.

routing-outlets-example-code

Code for the "add-comment" component.

Take notice of the Outlet usage in the code as well as the file structure. You will see that under "/dashboard" we have "/add-comment/" folder as well as "add-comment.tsx" file. Then under the "/add-comment/" folder we have "index.tsx" and "new.tsx" file.

The URL "localhost:3000/dashboard/add-comment" will render our "add-comment.tsx" component while also rendering the "/add-comment/index.tsx" component within the Outlet. However, if you navigate to the URL "localhost:3000/dashboard/add-comment/new" Then the Outlet will update and render the "/add-comment/new.tsx" component instead. Yeah, I couldn't even wrap my brain around that at first. This might take some getting used to but eventually it will make sense. Hopefully the below snippet will help you visualize it better.

URL -- localhost:3000/dashboard/add-comment
renders -- "add-comment.tsx"
Outlet renders -- "/add-comment/index.tsx"
Enter fullscreen mode Exit fullscreen mode
URL -- localhost:3000/dashboard/add-comment/new
renders -- "add-comment.tsx"
Outlet renders -- "/add-comment/new.tsx"
Enter fullscreen mode Exit fullscreen mode

You could also just organize your project in a way that allows you to have the same two components as child routes to achieve the same thing but then why is “Outlet” useful? It provides a way for you to pass down any UI state to your nested routes. That might be more difficult when your two components are in separate files and the parent component is not using “Outlet”. At least here Remix gives you options based on your needs.


routing-example-four

Going back to our admin example, the image above shows what your project structure would look like without using Outlets but trying to achieve the two routes for admin and normal user.

URL -- localhost:3000/dashboard
renders -- "index.tsx"
Enter fullscreen mode Exit fullscreen mode
URL -- localhost:3000/dashboard/admin
renders -- "admin.tsx"
Enter fullscreen mode Exit fullscreen mode

Overall, nested routes to me were a bit of a challenge to understand at first but slowly I realized how powerful it was and how great it really makes things work out. This is something you don't quite get out of the box with React. Then when combining React with React Router it makes your application that much more powerful. What is great about Remix though since it is from the same creators that built React Router, is that they use that under the hood of Remix too! Neat! Let us look at Actions next!

Actions

Actions are how Remix handles form data requests. As the user fills out the form and clicks submit then the action function processes the data. From there you can sanitize and save the input to the DB or anything else you need to do with that data. Remix even provides a way for you to add validation errors for your form inputs! I mentioned earlier about some React functionality in the browser. This is where some of that comes into the picture.

This image below shows what it might look like within an action function.

actions-example

The action function is auto wired, and Remix will know what to do with that function. When the form is submitted, this function gets executed. Line 12/13 is where we get the form data and, in this case, we grab “userInput” (you will see where “userInput” comes from later, but it is basically something I defined in the HTML output). Lines 15-23 is our error message handling. It is basic error handling currently. I have it checking if the value is null then display the error message "User input is required". In line 16 we define our errors and validations for each input. In line 18 we check to see if there were errors. Then at line 21 if there are any, we return the errors instead. Down at line 29 I return null because I didn't want to redirect anywhere but you could potentially return a redirect and navigate to another screen.

actions-example-two

In the above image we see the component and what it returns as html to the UI.

To wrap it all together this “New” component that I created renders the form that when submitted triggers our action function above. Line 40 is how we retrieve the errors if there are any then we display those errors in line 60. Line 42 we use transitions which is part of the React functionality in the browser I was referring to earlier. Line 43 follows that up and allows us to tell the UI that something is being processed after submitting the form so we can use that value to disable the button or do anything else we would like to. Notice on line 51 the name of the input, that is how we tie the input from the HTML code to what we are retrieving in the action above.

actions-example-three

This image above contains imports used.

The imports at the top are for React (Form, useActionData, and useTransition). You will also notice the Node ones (json) as well. Remix even provides you with TypeScript typing if you are using Typescript with Remix. There is no restriction however and you can most certainly write in plain JavaScript.

import { Form, useActionData, useTransition } from "@remix-run/react";
import type { ActionFunction } from "@remix-run/node";
import { json } from "@remix-run/node";

type ActionData =
    |   {
            userInput: null | string;
        }
    | undefined;

export const action: ActionFunction = async ({ request }) => {
    const formData = await request.formData();
    const userInput = formData.get("userInput");

    const errors: ActionData = {
        userInput: userInput ? null : "User input is required"
    };
    const hasErrors = Object.values(errors).some(
        (errorMessage) => errorMessage
    );
    if (hasErrors) {
        return json<ActionData>(errors);
    }

    // post to API

    // redirect
    // return redirect("/dashboard/add-comment")
    return null;
};

export default function New() {
    const errors = useActionData();

    const transition = useTransition();
    const isCreating = Boolean(transition.submission);

    return (
        <>
            <h2>Form</h2>
            <Form method="post">
                <input
                    type="text"
                    name="userInput"
                />
                <button
                    type="submit"
                    disabled={isCreating}
                >
                    {isCreating ? "Creating..." : "Create New"}
                </button>
            </Form>
            {errors?.userInput ? (<span>{errors.userInput}</span>) : null}
        </>
    );
}
Enter fullscreen mode Exit fullscreen mode

The code block above showcase the full file. Not bad right?

That is basically Remix Actions in a nutshell! I really am amazed at how simple it is to just get a form set up. It feels like a very different experience.

What is next?

That was a lot of remixing for now. I think what I have seen so far barely scratches the surface and I hope it helps get you excited about this framework a little bit more to want to explore it deeper. I know I enjoyed exploring what it has to offer and so far, I am liking the developer experience that it provides.

Some of the things that piqued my interest was one the idea of “Progressive enhancement”. By this they mean that the application you build works without JavaScript in the browser! Sounds crazy but I would like to learn more. From their tutorial they walk you through disabling JavaScript on your browser and you can still interact with the application! It’s quite fascinating but somewhat makes some sense as most of the code is on the server with very little JavaScript on the browser. They say it makes the UI more resilient to network issues. I am still a bit skeptical on that, but I will take their word for it. To me it sounds like your web apps built with Remix will run smoother on mobile without having to download so much JavaScript! Which means users in locations that don't support 4G or even 5G now can load your application faster! If that is what they mean by network issues, then that makes sense!

Another thing that also interests me is state management. We are all so use to using React state management libraries like Redux that I am curious what we would use here. Although as I write this and think about it, the LAMP stack didn't use state management and it didn't have to cause all the data came from the same server it was in. Your server is essentially the state manager. Maybe that applies here as well? I’m not too sure but I am excited to explore that a bit more.

There are so many other features it offers that I didn't discuss here such as the capability to give you SSR/CSR/SSG

Hopefully by me writing this and you reading it, I have piqued your interest a bit more on Remix! Thank you for reading all the way through. If you have scrolled all the way down here looking for the TLDR well...

TLDR:
Remix has a great developer experience and is super easy to get into. Their documentation is amazing! If you already know React and are itching to learn something new why not dive in?

Cover image credit: https://unsplash.com/photos/T3Neg57nlYs

Latest comments (2)

Collapse
 
farahmorris profile image
FarahMorris

We're going to build a simple React Date Picker from scratch. As our date manipulation helper, how about we use a library called "Day.js". دعاء يخلي زوجي يرجع لي

Collapse
 
raymondluudev profile image
Raymond Luu

Never used Day.js but it looks like a great alternative to Moment.js! Let me know how it went!