.server modules
On this page

.server modules

Summary

Server-only modules that are excluded from client bundles and only run on the server.

// This would expose secrets on the client
export const JWT_SECRET = process.env.JWT_SECRET;

export function validateToken(token: string) {
  // Server-only authentication logic
}

.server modules are a good way to explicitly mark entire modules as server-only. The build will fail if any code in a .server file or .server directory accidentally ends up in the client module graph.

Usage Patterns

Individual Files

Mark individual files as server-only by adding .server to the filename:

app/
├── auth.server.ts         👈 server-only file
├── database.server.ts
├── email.server.ts
└── root.tsx

Server Directories

Mark entire directories as server-only by using .server in the directory name:

app/
├── .server/               👈 entire directory is server-only
│   ├── auth.ts
│   ├── database.ts
│   └── email.ts
├── components/
└── root.tsx

Examples

Database Connection

import { PrismaClient } from "@prisma/client";

// This would expose database credentials on the client
const db = new PrismaClient({
  datasources: {
    db: {
      url: process.env.DATABASE_URL,
    },
  },
});

export { db };

Authentication Utilities

import jwt from "jsonwebtoken";
import bcrypt from "bcryptjs";

const JWT_SECRET = process.env.JWT_SECRET!;

export function hashPassword(password: string) {
  return bcrypt.hash(password, 10);
}

export function verifyPassword(
  password: string,
  hash: string
) {
  return bcrypt.compare(password, hash);
}

export function createToken(userId: string) {
  return jwt.sign({ userId }, JWT_SECRET, {
    expiresIn: "7d",
  });
}

export function verifyToken(token: string) {
  return jwt.verify(token, JWT_SECRET) as {
    userId: string;
  };
}

Using Server Modules

import type { ActionFunctionArgs } from "react-router";
import { redirect } from "react-router";
import {
  hashPassword,
  createToken,
} from "../utils/auth.server";
import { db } from "../utils/db.server";

export async function action({
  request,
}: ActionFunctionArgs) {
  const formData = await request.formData();
  const email = formData.get("email") as string;
  const password = formData.get("password") as string;

  // Server-only operations
  const hashedPassword = await hashPassword(password);
  const user = await db.user.create({
    data: { email, password: hashedPassword },
  });

  const token = createToken(user.id);

  return redirect("/dashboard", {
    headers: {
      "Set-Cookie": `token=${token}; HttpOnly; Secure; SameSite=Strict`,
    },
  });
}

export default function Login() {
  return (
    <form method="post">
      <input name="email" type="email" required />
      <input name="password" type="password" required />
      <button type="submit">Login</button>
    </form>
  );
}
Docs and examples CC 4.0
Edit