Skip to main content

Overview

The z.nanoid() function validates NanoIDs - compact, URL-safe unique identifiers designed as a smaller, faster alternative to UUIDs.
import { z } from "zod";

const nanoidSchema = z.nanoid();
Location in source: ~/workspace/source/packages/zod/src/v4/classic/schemas.ts:549

Basic Usage

const schema = z.nanoid();

schema.parse("lfNZluvAxMkf7Q8C5H-QS"); // ✓ passes
schema.parse("invalid nanoid"); // ✗ throws

Validation Rules

A valid NanoID must:
  1. Be exactly 21 characters long
  2. Contain only: a-z, A-Z, 0-9, _ (underscore), - (hyphen)
  3. Match pattern: /^[a-zA-Z0-9_-]{21}$/

Valid Examples

const schema = z.nanoid();

schema.parse("lfNZluvAxMkf7Q8C5H-QS"); // ✓ passes
schema.parse("mIU_4PJWikaU8fMbmkouz"); // ✓ passes
schema.parse("Hb9ZUtUa2JDm_dD-47EGv"); // ✓ passes
schema.parse("5Noocgv_8vQ9oPijj4ioQ"); // ✓ passes
schema.parse("ySh_984wpDUu7IQRrLXAp"); // ✓ passes

Invalid Examples

const schema = z.nanoid();

// Too long (22 characters)
schema.safeParse("Xq90uDyhddC53KsoASYJGX").success; // false

// Contains spaces
schema.safeParse("invalid nanoid").success; // false

// Too short
schema.safeParse("lfNZluvAxMkf7Q8C5H-Q").success; // false

// Invalid characters
schema.safeParse("lfNZluvAxMkf7Q8C5H QS").success; // false
schema.safeParse("lfNZluvAxMkf7Q8C5H@QS").success; // false

Custom Error Message

const schema = z.nanoid("Please provide a valid NanoID");

const result = schema.safeParse("invalid");
if (!result.success) {
  console.log(result.error.issues[0].message);
  // "Please provide a valid NanoID"
}

Error Details

When validation fails, the error includes the expected pattern:
const schema = z.nanoid("custom error");

const result = schema.safeParse("Xq90uDyhddC53KsoASYJGX");
if (!result.success) {
  console.log(result.error.issues[0]);
  /*
  {
    origin: "string",
    code: "invalid_format",
    format: "nanoid",
    pattern: "/^[a-zA-Z0-9_-]{21}$/",
    path: [],
    message: "custom error"
  }
  */
}

Parameters

z.nanoid(params?: string | NanoIDParams)
  • string: Custom error message
  • NanoIDParams: Object with:
    • message?: string: Custom error message

Examples

Database Primary Key

const postSchema = z.object({
  id: z.nanoid(),
  title: z.string(),
  content: z.string(),
  authorId: z.nanoid(),
});

postSchema.parse({
  id: "lfNZluvAxMkf7Q8C5H-QS",
  title: "My Post",
  content: "Post content here",
  authorId: "mIU_4PJWikaU8fMbmkouz"
}); // ✓ passes

API Request Tracking

const requestSchema = z.object({
  requestId: z.nanoid(),
  timestamp: z.date(),
  data: z.record(z.unknown()),
});

const request = requestSchema.parse({
  requestId: "Hb9ZUtUa2JDm_dD-47EGv",
  timestamp: new Date(),
  data: { foo: "bar" }
});

URL-Safe Identifiers

const shortLinkSchema = z.object({
  slug: z.nanoid(),
  url: z.url(),
});

shortLinkSchema.parse({
  slug: "5Noocgv_8vQ9oPijj4ioQ",
  url: "https://example.com/very-long-url"
});

// Can be used in URLs safely
const shortUrl = `https://short.link/${slug}`;
// https://short.link/5Noocgv_8vQ9oPijj4ioQ

Form Validation

const formSchema = z.object({
  sessionId: z.nanoid("Invalid session ID"),
  userId: z.nanoid("Invalid user ID"),
  action: z.string(),
});

const result = formSchema.safeParse({
  sessionId: "invalid",
  userId: "lfNZluvAxMkf7Q8C5H-QS",
  action: "submit"
});
// Returns error for invalid sessionId

Optional NanoID

const schema = z.object({
  id: z.nanoid(),
  parentId: z.nanoid().optional(),
});

schema.parse({
  id: "lfNZluvAxMkf7Q8C5H-QS",
  // parentId is optional
}); // ✓ passes

Return Type

Returns a ZodNanoID schema that validates and returns strings.
type Output = string;
type Input = string;

NanoID Characteristics

Advantages

  • Compact: Only 21 characters (vs 36 for UUIDs)
  • URL-safe: No special encoding needed
  • Fast: Simple generation algorithm
  • Collision-resistant: 2^126 unique IDs (vs 2^122 for UUID v4)
  • Readable: Uses a larger alphabet than hex

Limitations

  • Fixed length: Always 21 characters (not configurable in validator)
  • Not sortable: Random generation, no time component
  • Case-sensitive: Must preserve exact casing
NanoIDs are designed to be URL-safe and don’t require special encoding when used in URLs, making them ideal for public-facing identifiers like short links, share URLs, and API endpoints.

Use Cases

When to Use NanoID

  • Short URLs and slugs
  • Public-facing IDs
  • Session identifiers
  • API keys (combined with other security measures)
  • Database primary keys (when size matters)
  • Client-generated IDs
  • Mobile applications (smaller payload)

When NOT to Use NanoID

  • When you need sortable IDs (use UUID v7 or ULID)
  • When you need specific length requirements
  • When case-insensitivity is required
  • Legacy systems expecting UUIDs
The validator checks for the standard 21-character NanoID format. If your application uses custom NanoID lengths or alphabets, you’ll need to create a custom validator using z.stringFormat() or z.string().regex().

Comparison with Other IDs

FeatureNanoIDUUID v4CUID2
Length213624-32
URL-safeYesNo (hyphens)Yes
SortableNoNoNo
Collision resistanceHighHighHigh
Size (bytes)213624-32

Build docs developers (and LLMs) love