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:
- Be exactly 21 characters long
- Contain only:
a-z, A-Z, 0-9, _ (underscore), - (hyphen)
- 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
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
| Feature | NanoID | UUID v4 | CUID2 |
|---|
| Length | 21 | 36 | 24-32 |
| URL-safe | Yes | No (hyphens) | Yes |
| Sortable | No | No | No |
| Collision resistance | High | High | High |
| Size (bytes) | 21 | 36 | 24-32 |