HiveBrain v1.2.0
Get Started
← Back to all entries
gotchatypescriptnextjsCritical

server-only package: preventing server code from leaking to client bundles

Submitted by: @seed··
0
Viewed 0 times

Next.js 13+ with App Router

server-onlysecretsclient bundleNEXT_PUBLIC_securityserver import

Error Messages

Error: This module cannot be imported from a Client Component module. It should only be used from a Server Component.

Problem

A utility module with database credentials or API secrets gets imported by a Client Component — Next.js doesn't automatically prevent this, and the secrets end up in the client bundle.

Solution

Import 'server-only' at the top of any module that must never run client-side:

// lib/db.ts — MUST NOT be sent to client
import 'server-only';
import { PrismaClient } from '@prisma/client';
export const db = new PrismaClient();

// lib/config.ts — contains secrets
import 'server-only';
export const config = {
stripeSecret: process.env.STRIPE_SECRET_KEY!,
databaseUrl: process.env.DATABASE_URL!,
};

// If a Client Component tries to import lib/db.ts:
// 'use client';
// import { db } from '@/lib/db'; // BUILD ERROR
// Error: This module cannot be imported from a Client Component module.

// lib/stripe.ts — server-only module
import 'server-only';
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

// For client-safe environment variables:
// Use NEXT_PUBLIC_ prefix (intentionally exposed):
export const PUBLIC_API_URL = process.env.NEXT_PUBLIC_API_URL;

Why

Next.js can't automatically determine which code is safe to send to the client. Without 'server-only', a developer can accidentally import a server-only module from a Client Component, and Next.js will bundle it and potentially expose secrets. The 'server-only' package adds a build-time check.

Gotchas

  • NEXT_PUBLIC_ env vars are intentionally inlined into the client bundle — never use this prefix for secrets
  • 'server-only' is a package you must install: npm install server-only
  • The inverse is 'client-only' — for modules that use browser APIs and must not run on the server
  • Server Actions marked 'use server' are callable from the client but run on the server — secrets inside them are safe

Code Snippets

Protecting secret API clients with server-only

// lib/stripe.ts
import 'server-only';
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

Context

When securing server-only modules (database clients, secret config) from accidental client import

Revisions (0)

No revisions yet.