Remix 101

Université de Toulon

LIS UMR CNRS 7020

2024-11-03

Objective

  • The objective of this part is to serve static HTML and CSS pages using Remix.
  • Remix is a full-stack web framework that focuses on performance and user experience.
  • It allows developers to build modern web applications with a focus on speed and simplicity.

Key Features and Benefits

  • Server-Side Rendering (SSR): Improves performance and SEO.
  • Nested Routes: Simplifies complex routing scenarios.
  • Data Loading: Efficient data fetching and caching.
  • Error Boundaries: Enhanced error handling.
  • Built-in CSS Support: Simplifies styling.

Prerequisites (Node.js, npm)

  • Ensure you have Node.js and npm installed on your machine.
    • Node.js: A JavaScript runtime built on Chrome’s V8 JavaScript engine that allows you to run JavaScript on the server-side.
    • npm: The Node Package Manager, which is used to install and manage packages (libraries and tools) for Node.js applications.
    • see Node.js for installation instructions.

Setting Up a Minimal Remix Project

  • Blues Stack Template: The Blues Stack is a starter template for Remix that comes pre-configured with essential tools and libraries such as Prisma for database management, Tailwind CSS for styling, and other utilities to help you quickly set up a full-featured Remix project.
  • Why Use Blues Stack?
    • Quick Setup: Provides a ready-to-use project structure, saving time on initial setup.
    • Best Practices: Incorporates best practices for modern web development.
    • Integrated Tools: Comes with Prisma for database management and Tailwind CSS for styling, among other utilities.
    • Scalability: Designed to scale with your project as it grows, making it easier to manage and maintain.
  • Installation and Project Setup
    • Run the following command to create a new Remix project using the Blues Stack template:

      npx create-remix --template remix-run/blues-stack my-app
    • This command will create a new folder named my-app and set up the entire project after you answer yes to a couple of questions.

Note

Create a sample project using the Blues Stack template to get started with Remix. You can change the project name my-app to any name you prefer.

Detailed Project Structure

  • Public Directory:
    • Purpose: Stores static assets.
    • Contents:
      • Icons, images, and other static files that your application needs.
  • App Directory:
    • Purpose: Contains the main application code.
    • Contents:
      • Routes: Define the different pages and API endpoints of your application.
      • Components: Reusable UI components.
      • Styles: Tailwind CSS configuration and custom styles.
      • Utils: Utility functions and helpers.
      • Data: Database access and data fetching logic.
  • Prisma Directory:
    • Purpose: Manages database schema and migrations.
    • Contents:
      • schema.prisma: Defines your database schema.
      • Migration files: Track changes to your database schema over time.

Starting the Development Server

Database Configuration

  • The Blues Stack uses PostgreSQL as the database. You need to set up a PostgreSQL database and configure the connection in the .env file.
  • Update the DATABASE_URL in the .env file with your database credentials: DATABASE_URL="postgresql://<USERNAME>:<PASSWORD>@localhost:5432/<DATABASE_NAME>"

Avertissement

The database connection is not required to serve static HTML and CSS pages using Remix. Database connection is covered in a separate lecture.

Initial Setup

  • Before starting the development server, you need to set up the project dependencies and environment variables.

  • Run the following command to install dependencies and set up the environment:

    npm run setup

Note

Run the setup.

Run the First Build

  • After setting up the project, you need to build it for the first time. This will compile the project and prepare it for development:

    npm run build

Note

Run the build.

Start the Development Server

  • Finally, start the development server to run your project locally:

    npm run dev
  • This will start the server, and you can view your project by opening a web browser and going to http://localhost:3000.

  • Development URL:

    • Open your web browser and navigate to:

      http://localhost:3000
    • You should see a Remix application to manage notes running locally.

By following these steps, you will have your development server up and running, allowing you to view and interact with your Remix project in real-time.

Note

Run the development server and open the web site.

Adding Static Files to the Project

  • Remix allows you to serve static HTML and CSS files directly from the public directory. This feature is useful for serving standalone pages or assets that do not require dynamic content generation.

  • Static files such as HTML, CSS, images, and other assets should be placed in the public directory. This directory is automatically served by Remix, making the files accessible via the root URL.

  • It’s a good practice to organize your static files into subdirectories within the public directory. For example:

    • public/images for image files.
    • public/css for additional CSS files.
    • public/js for JavaScript files.

Example: Adding Static HTML

  • Create an HTML file named simple.html in the public directory:

    <!-- public/simple.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Static Page</title>
      <link rel="stylesheet" href="/css/styles.css">
    </head>
    <body>
      <h1>Welcome to My Static Page</h1>
    </body>
    </html>

Example: Adding Static CSS

  • Create a CSS file named styles.css in the public/css directory:

    /* public/css/styles.css */
    body {
      font-family: Arial, sans-serif;
      background-color: #f0f0f0;
      margin: 0;
      padding: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
    }
    h1 {
      color: #333;
    }

Example: Adding an Image

  • Place an image file named logo.png in the public/images directory:

    <!-- public/simple.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Static Page</title>
      <link rel="stylesheet" href="/css/styles.css">
    </head>
    <body>
      <h1>Welcome to My Static Page</h1>
      <img src="/images/logo.png" alt="Logo">
    </body>
    </html>
  • Once the files are in the public directory, you can access them directly via the browser. For example:

    • Open your web browser and navigate to http://localhost:3000/index.html to view the static HTML page.
    • The CSS file will be automatically applied to the HTML page because it is linked in the <head> section of the HTML file.
    • The image file can be accessed using the relative path /images/logo.png in the HTML file.

Team Project

Note

Add and organize static files for your project in the public directory of your Remix project to serve HTML, CSS, images, and other assets.

Serving HTML and CSS from Remix

What is a Route? - A route in Remix (and web development in general) is a URL path that corresponds to a specific page or resource in your application. - Routes determine what content is displayed when a user navigates to a particular URL. - Routes are defined by creating files in the app/routes directory.

Example Route

// app/routes/welcome.tsx
export default function Welcome() {
  return (
    <div className="flex flex-col items-center justify-center h-screen bg-gray-100 text-gray-800">
      <h1 className="text-4xl">Hello, World!</h1>
      <p className="text-xl mt-2">Welcome to our website. We're glad to have you here!</p>
    </div>
  );
}
  • This exemple uses Tailwind CSS classes to style the HTML elements (blues stack includes Tailwind).
  • Place the example code in a file named welcome.tsx within the app/routes directory of your Remix project.
  • To access this page, navigate to http://localhost:3000/welcome in your web browser.

Dynamic HTML Page in Javascript with Remix

  • Remix allows to build dynamic HTML page using Javascript (see next lecture) on the server side.
  • The page is rendered on the server side and sent to the client as a complete HTML document.
  • In the next lectures, we will see how to fetch data from a database or API and render it dynamically on the server side.

Dynamic Route with Data Fetching

// app/routes/dynamicProducts.tsx
import { useLoaderData } from "remix";
import { json } from "remix";

// Simulate fetching data from a database or API
const fetchProducts = async () => {
  return [
    { id: 1, name: "Product 1", price: "$10" },
    { id: 2, name: "Product 2", price: "$20" },
    { id: 3, name: "Product 3", price: "$30" },
  ];
};

// Loader function to fetch data on the server side
export const loader = async () => {
  const products = await fetchProducts();
  return json({ products });
};

// React component to display the products
export default function Products() {
  const { products } = useLoaderData();

  return (
    <div className="flex flex-col items-center justify-center h-screen bg-gray-100 text-gray-800">
      <h1 className="text-4xl mb-4">Products</h1>
      <ul className="space-y-2">
        {products.map((product) => (
          <li key={product.id} className="text-xl">
            {product.name} - {product.price}
          </li>
        ))}
      </ul>
    </div>
  );
}
  • This example fetches product data and renders it dynamically on the server side.
  • Place the example code in a file named dynamicProducts.tsx within the app/routes directory of your Remix project.
  • To access this page, navigate to http://localhost:3000/dynamicProducts in your web browser.

Where Does the Rest of the HTML Page Come From?

  • The rest of the HTML page is generated by Remix’s server-side rendering (SSR) capabilities.
  • Remix automatically includes the necessary HTML structure, such as <html>, <head>, and <body> tags, around your route components.
  • You can customize the HTML structure by modifying the app/root.tsx file, which serves as the root layout for your application.

Understanding root.tsx

  • The root.tsx file in a Remix project serves as the root layout for your application.
  • It defines the overall structure of your HTML document, including the <html>, <head>, and <body> tags.
  • You can use this file to include global components, such as a navigation bar or footer, that should be present on every page.

Example of a basic root.tsx file:

// app/root.tsx
import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from "remix";

export default function App() {
  return (
    <html lang="en">
      <head>
        <Meta />
        <Links />
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}
  • The <Outlet /> component renders the matched child route component.
  • The <Links />, <Meta />, <Scripts />, and <LiveReload /> components are provided by Remix to handle various aspects of the HTML document and live reloading during development.

Creating and Organizing Components

What is a Component? - In Remix, a component is a reusable piece of UI that can be used across different parts of your application. - Components help in breaking down the UI into smaller, manageable pieces, making the code more modular and easier to maintain.

Example Component

// app/components/Navbar.tsx
export default function Navbar() {
  return (
    <nav className="bg-blue-600 p-4 shadow-md">
      <ul className="flex space-x-4">
        <li><a href="/" className="text-white hover:text-gray-300">Home</a></li>
        <li><a href="/products" className="text-white hover:text-gray-300">Products</a></li>
        <li><a href="/dashboard" className="text-white hover:text-gray-300">Dashboard</a></li>
        <li><a href="/dashboard/settings" className="text-white hover:text-gray-300">Dashboard Settings</a></li>
      </ul>
    </nav>
  );
}
  • Place the example code in a file named Navbar.tsx within the app/components directory (create it if needed) of your Remix project.

Using Components in Routes

  • To use a component in a route, import it and include it in the JSX of your route component.

    // app/routes/products.tsx
    import Navbar from "~/components/Navbar";
    
    export default function Products() {
      return (
        <div className="min-h-screen bg-gray-100">
          <Navbar />
          <div className="container mx-auto p-4">
            <h1 className="text-3xl font-bold mb-4">Products Page</h1>
            <p className="text-lg text-gray-700">Details about the products will go here.</p>
          </div>
        </div>
      );
    }

Organizing Components - Organize your components in the app/components directory. - Group related components into subdirectories for better organization. - Example directory structure: app/ ├── components/ │ ├── Navbar.tsx │ └── Footer.tsx ├── routes/ │ ├── index.tsx │ └── products.tsx

Understanding Nested Routes in Remix (Advanced)

What are Nested Routes? - Nested routes allow you to create complex layouts by nesting route components within each other. - They enable you to build hierarchical structures where child routes inherit the layout and context of their parent routes.

Benefits of Nested Routes - Reusability: Share common layouts and components across multiple routes. - Maintainability: Simplify the management of complex UIs by breaking them into smaller, manageable pieces. - Consistency: Ensure a consistent look and feel across different parts of your application.

Example of Nested Routes

// app/routes/dashboard.tsx

import { Outlet } from "@remix-run/react";
import Navbar from "~/components/Navbar";

export default function Dashboard() {
  return (
    <div className="min-h-screen bg-gray-100">
      <Navbar />
      <div className="container mx-auto p-4">
        <h1 className="text-4xl font-bold mb-4">Dashboard</h1>
        <Outlet />
      </div>
    </div>
  );
}

// app/routes/dashboard.index.tsx
export default function DashboardIndex() {
  return (
    <div className="p-4 bg-white shadow-md rounded-md">
      <h2 className="text-2xl font-semibold mb-2">Welcome to the Dashboard</h2>
      <p className="text-gray-700">This is the main dashboard page.</p>
    </div>
  );
}

// app/routes/dashboard.settings.tsx
export default function DashboardSettings() {
  return (
    <div className="p-4 bg-white shadow-md rounded-md">
      <h2 className="text-2xl font-semibold mb-2">Settings</h2>
      <p className="text-gray-700">Manage your settings here.</p>
    </div>
  );
}

How to Define Nested Routes - Create a parent route file (e.g., dashboard.tsx) that includes the <Outlet /> component. - Create child route files (e.g., dashboard.index.tsx and dashboard.settings.tsx) within the same directory as the parent route. - The child routes will be rendered inside the <Outlet /> component of the parent route.

Accessing Nested Routes - Navigate to the parent route URL to access the nested routes. - Example: - http://localhost:3000/dashboard will render the Dashboard component and the DashboardIndex component. - http://localhost:3000/dashboard/settings will render the Dashboard component and the DashboardSettings component.

Parameters in Remix

What are Parameters? - Parameters are dynamic segments of the URL that can be used to capture values and pass them to your components. - They allow you to create dynamic routes that can handle different values.

Defining Parameters in Routes - Use the : prefix to define a parameter in your route path. - Example: routes/products.$productId.tsx where $productId is a parameter.

Accessing Parameters in Components - Use the useParams hook from Remix to access the parameters in your component. - Example: ```typescript // app/routes/products.$productId.tsx import { useParams } from ‘@remix-run/react’;

export default function Product() { const { productId } = useParams(); return (
    <h2 className="text-2xl font-semibold mb-2">Product ID: {productId}</h2>
    <p className="text-gray-700">Details about product {productId}.</p>
  </div>
);

} ```

Example Usage - Navigate to http://localhost:3000/products/123 to see the product with ID 123. - The Product component will render with the productId parameter value.

Benefits of Using Parameters - Dynamic Routing: Create flexible routes that can handle different values. - Simplified Code: Easily access and use parameter values in your components. - Improved User Experience: Provide users with dynamic and personalized content based on the URL parameters.

Client-Side Routing in Remix

What is Client-Side Routing? - Client-side routing allows navigation between different parts of your application without a full page reload. - It provides a smoother and faster user experience by updating the URL and rendering the appropriate components dynamically.

Benefits of Client-Side Routing - Performance: Reduces the need for full page reloads, resulting in faster navigation. - User Experience: Provides a seamless and smooth transition between pages. - State Preservation: Maintains the application state during navigation.

Using the <Link> Component - Remix provides the <Link> component for client-side navigation. - Example: ```typescript // app/routes/index.tsx import { Link } from “remix”;

export default function Index() { return (
    <h1>Welcome to the Home Page</h1>
    <nav>
      <ul>
        <li><Link to="/about">About</Link></li>
        <li><Link to="/products">Products</Link></li>
      </ul>
    </nav>
  </div>
);

} ```

Remix Lifecycle

  1. Request Handling:
    • Remix receives an HTTP request and matches it to a route.
    • The matched route determines the component to render and the data to fetch.
  2. Data Loading:
    • Loaders are used to fetch data required by the route.
    • Data is fetched on the server-side and passed to the component.
  3. Rendering:
    • The component is rendered on the server-side (SSR) or client-side.
    • Server-side rendering improves performance and SEO.
  4. Error Handling:
    • Error boundaries catch errors during rendering and data loading.
    • Provides a fallback UI to display in case of errors.
  5. Client-Side Navigation:
    • Remix manages client-side navigation using the browser’s history API.
    • Updates the UI without full page reloads, providing a smooth user experience.

Summary

  • Remix is a powerful framework for building modern web applications with a focus on performance and user experience.
  • Key features include server-side rendering, nested routes, efficient data loading, error boundaries, and built-in CSS support.
  • Setting up a Remix project involves creating a new project, configuring the database, and starting the development server.
  • Static files can be served from the public directory, and routes are defined in the app/routes directory.
  • Components can be created and organized in the app/components directory for better reusability and maintainability.

Additional Resources