Modals with Remix

modals-with-remix

Modals with Remix

Using Remix I have found it second nature to use modal routes when I can. Instead of using useState and passing data to the modal component, a modal route can be created under the parent route, and it being a route there is no need to pass data that may not be used if a user chooses not to open the modal. Less mess, more simplicity.

Since it is a route, you can use loader as well action, I have found that for most cases useState is unnecessary, and more messy when using Remix.

In this post I’ll create a modal route that creates a user entry in the database to portray how simple and effortless using Remix can be. Much less time thinking about how to do something, more time spent on doing what is important.

First we’ll initialize Prisma by creating a file called db.server.ts

// app/db.server.ts

import { PrismaClient } from "@prisma/client"                  

declare global {
  var __prisma: PrismaClient
}

if (!global.__prisma) {
  global.__prisma = new PrismaClient()
}
global.__prisma.$connect()

export const prisma = global.__prisma
// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Post {
  id    Int     @id @default(autoincrement())
  title String
  name  String
  date_created DateTime @default(now())
  date_updated DateTime @default(now())
}

We’ll also create a route file called posts.tsx, we’ll render the posts from the database here, we use a loader to pass the data from the database to the user:

import type {LoaderFunctionArgs, MetaFunction} from "@remix-run/node";
import {prisma} from "~/db.server";
import {Link, Outlet, useLoaderData} from "@remix-run/react";

export const meta: MetaFunction = () => {
  return [
    { title: "New Remix App" },
    { name: "description", content: "Welcome to Remix!" },
  ];
};

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

  return (
    
{posts.map(post => )}
Post ID Title Name Last updated Actions
{post.id} {post.title} {post.name} {post.date_updated} Edit
); } export async function loader({request}: LoaderFunctionArgs) { const posts = await prisma.post.findMany() return {posts} } } export async function loader({request}: LoaderFunctionArgs) { const posts = await prisma.post.findMany() return {posts} }

This will render all the posts from the database, first we will deal with the first modal route that creates a post in the database.

First we must create a file posts.create.tsx, but naming a file like that is not enough for the modal to appear on top of posts, an must exist inside the posts.tsx route.

// posts.create.tsx
import {useEffect} from "react";
import {Form, Link, redirect} from "@remix-run/react";
import {ActionFunctionArgs} from "@remix-run/node";
import {z} from "zod";
import {zx} from "zodix";
import {prisma} from "~/db.server";

export default function Create() {
    return 
}

You will notice as it is often with Remix that there is no JS here, closing the modal simply takes you back to /posts which is the parent route.

Now we will add an action which will create a new post when the user submits the form. We will validate the request body with Zodix, a module that allows us to use Zod to parse FormData.

function slugify(str: string) {
    str = str.replace(/^s+|s+$/g, ''); // trim leading/trailing white space
    str = str.toLowerCase(); // convert string to lowercase
    str = str.replace(/[^a-z0-9 -]/g, '') // remove any non-alphanumeric characters
        .replace(/s+/g, '-') // replace spaces with hyphens
        .replace(/-+/g, '-'); // remove consecutive hyphens
    return str;
}

export async function action({request}: ActionFunctionArgs) {
    const { title } = await zx.parseForm(request, {
        title: z.string(),
    });

    const slug = slugify(title);

    await prisma.post.create({
        data: {
            title,
            name: slug
        }
    })


    return redirect("https://dev.to/posts?created=true")
}

We will redirect the user to /posts with a URL param that will let the user know that the post has been created successfully, that is often times achieved with session.flash but as this is a simple example this suffices.

// app/root.tsx

const [searchParams, setSearchParams] = useSearchParams()

useEffect(() => {
        if (searchParams.get('created')) {
            alert("Post created")
            const params = new URLSearchParams();
            params.delete("created");
            setSearchParams(params, {
                preventScrollReset: true,
            });
        }
}, [searchParams])

And the result is simplicity of a modal route, no need to have a state to manage the modal, or an API route to handle the creation of a post. The code is more clean and organized, and with a modal route like posts.create.tsx the action is found in that file as well, and it handles the task of creating a post entirely.

A modal route has many different possibilities, user settings, editing posts, creating posts. And that is the appeal of a modal route, simplicity in code and less clutter.

You can find the source code here: https://github.com/ddm50/modals-with-remix

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
dev:-game

Dev: Game

Next Post
how-to-create-a-virtual-machine-that-is-highly-available.

How to create a Virtual Machine that is highly available.

Related Posts