Introduction to Remix: A Fullstack React Framework
What is Remix?
Remix is a React framework designed for server-side rendering (SSR). Positioned as a full-stack web framework, allowing developers to build both backend and frontend within a single app. Unlike vanilla React, where data is fetched on the frontend and then rendered on the screen, Remix fetches data on the backend and serves the HTML directly to the user. It provides fully rendered pages on initial load, offering a quick and complete user interface experience.
One of the important concepts in Remix is routing. Kent C. Dodds, the director of developer experience at Remix once described it as:
“[Remix is basically like] v7 of React Router. It’s like an upgrade of React Router that supports server rendering. It’s a compiler.”
Remix was made open-source in October 2021, and was acquired by Shopify in 2022.
Installation and Project Structure
Use this command line to get started with creating a remix app:
npx create-remix@latest name-of-your-app
The directory of the app contains the following folders and files. Among them, entry.client.jsx and entry.server.jsx are managed by Remix, and it’s better not to edit them.
- The app folder contains our main app logic.
- All the folders and files under the routes folder are exposed to the public and can be accessed with a URL.
- app/root.tsx is the root of our project. It describes the structure of the HTML on our index page. It also contains the general page layout.
- entry.client.tsx file gives us full control over the “hydrate” step (taking something static like HTML and connecting it with JavaScript to make it dynamic) after JavaScript loads into the document.
- entry.server.tsx file allows us to handle requests and render the entire server app. An entry server takes all the components in the application and renders them as a string, giving us full control over the way the markup is generated and sent to the client.
- The public folder contains public assets like static images and favicons.
- The build folder contains assets generated during the build process.
- remix.config.ts is where we configure our remix project, such as the app directory, which we can change from app to something else if we don’t like the name app, or publicPath, the port it should run on in development mode, and so on.
- remix.env.d.ts and tsconfig.json allow us to reference some types and contain some compiler options.
Routing and Layout
Pages inside the routes folder in Remix are nested within the route instead of being separate, allowing components to be embedded into the parent page.
Subroutes can be created directly inside the routes folder. To create a parent route, a folder with the route name should be created inside the routes folder, containing an index.tsx file.
Nested Routes
You can create nested routes by using dot delimiters. If the part of the filename before the dot matches another route filename, it automatically becomes a sub-route under the corresponding parent.
When introducing nested routes, including an index route (with a leading underscore in the file name) can ensure that content is displayed within the parent’s outlet when users directly visit the parent URL.
Nested Layouts
Nested layouts are used to avoid repeating the same style for multiple pages. When a file with the same name as the folder exists inside the routes directory at the same level, it becomes the layout for that folder.
To opt out of nesting for one of the child routes, you can add a trailing underscore on the parent segment.
Nested routing involves mapping routes to segments of the URL, and creating a common UI for each route. For example, the Root component in Remix maps to the root URL, representing the common UI across all routes, while the Outlet component acts as a wrapper for nested routes, creating a space to be filled by child routes.
Remix optimizes navigation by updating only the section of the UI or the data that needs to change, rerendering persistent components across URLs and reducing the loading time. Data dependencies, actions, and styling can also be mapped to nested routes, allowing efficient fetching of data for changing routes.
Fullstack Data Flow
Bridging the Client Side and the Server Side
In an application, there is typically code that runs on the client and the server. To run things in the client, data needs to be fetched and processed on the frontend. This leaves the developers to juggle asynchronous code, cache data using React states, and deal with difficult bugs and larger bundle sizes.
Remix addresses the gap between client and server code execution by allowing developers to write both frontend and backend code in the same file, simplifying the process of moving code from the client to the server and only sending what the client needs over the chasm. This approach reduces the time and cost associated with crossing this gap, minimizing loading spinners and streamlining asynchronous code management.
Data Loading
With the loader function and the useLoaderData hook, Remix decouples fetch initiation and result reading for optimization. By co-locating the loader and component, easy sharing of TypeScript types between them is also enabled.
Similar to Next.js’ getServerSideProps, Remix utilizes a loader function that runs on the server side when a page is requested, taking a native Request object from the Fetch API (This is part of the Remix philosophy that it emphasizes using native web technologies).
The useLoaderData hook then retrieves data returned by the loader function for rendering on the frontend, and it can be used from any component in the current route. Additionally, Remix can use this to prefetch data for routes before a user clicks a link, providing a snappy navigation experience.
State Management
Although Remix supports the integration of various React libraries, including state management tools like Redux and Recoil, its server-side rendering nature can lead to state loss during page refreshes or transitions.
Therefore, in such server-side rendered applications, state management is often sidestepped in favour of storing data, like user tokens, in cookies. This data is then sent to servers, which respond with user data through props to the page. This is because the server cannot read the browser’s local storage.
Form Handling
Instead of relying on modern techniques like onClick or onSubmit with HTTP calls, Remix provides functions such as action and loader for server-side operations, similar to the more traditional approach of using PHP for form handling.
The action function is exported by the route and called upon form submission, and it receives a Request object, allowing the retrieval of form data in the standard manner.
The useActionData hook is for working with data associated with the action function. These functions effortlessly access form data, eliminating the need to send JavaScript to the frontend for form submission.
Form Component
Remix introduces a Form component, an enhanced version of the standard HTML form element, designed with progressive enhancement principles. Progressive enhancement is a strategy in web design to provide a functioning app to as many users as possible, while also providing each user with the best possible experience based on their browsers’ features.
This approach ensures that the form remains functional even for users without JavaScript or those with slow internet connections, as it still sends requests to the server. For users with JavaScript, the form is further enhanced using fetch, providing a smoother experience without page refreshes on form submission.
Here is an example of a form using the POST method. Upon submission, it sends a POST request to the route matching its action attribute:
The information from the form fields can be accessed using the standard request.formData() API, and the name attribute on the inputs corresponds to the formData.get(fieldName) getter.
This co-location of components, data reads, and data writes simplifies error handling in the action — any encountered unhappy paths involve returning or throwing a response, while Remix manages the rest.
References
- Sample Code Folder: https://github.com/makoto357/remix-website
- Remix: A guide to the React framework taking on Next.js: https://blog.logrocket.com/guide-to-remix-react-framework/
- A Look at Remix React Framework: The Full Stack Developer’s Guide: https://betterprogramming.pub/end-to-end-guide-to-remix-a-full-stack-web-framework-8dc56eb6a9c7
- A Beginner’s Introduction to Remix: https://www.mattstobbs.com/introduction-to-remix/
- Is Remix Framework the Future of Web Development? https://geekflare.com/remix-framework/
- A Complete Guide to RemixJS — Create Your First Remix Project: https://www.geeksveda.com/create-remix-app/
- Getting Started with Remix Framework: https://thecodest.co/blog/getting-started-with-the-remix-framework/
- Fullstack Data Flow (Remix Official Document): https://remix.run/docs/en/main/discussion/data-flow
- Route File Naming (Remix Official Document): https://remix.run/docs/en/main/file-conventions/routes#nested-routes