next-safe-action
Integrations

Standard Schema

next-safe-action supports any validation library that implements the Standard Schema specification (v1). This means you can use Zod, Valibot, ArkType, or any other compliant library, with no adapters or plugins needed.

Supported libraries

Any library that implements the StandardSchemaV1 interface will work with next-safe-action. Check the Standard Schema repository for the full list of compliant libraries.

Usage

The API is identical regardless of which library you choose. Just pass your schema to .inputSchema() and .outputSchema():

src/app/actions.ts
"use server";

import { z } from "zod";
import { actionClient } from "@/lib/safe-action";

export const createUser = actionClient
	.inputSchema(
		z.object({
			name: z.string().min(2),
			email: z.string().email(),
		})
	)
	.action(async ({ parsedInput }) => {
		// parsedInput: { name: string, email: string }
	});
src/app/actions.ts
"use server";

import * as v from "valibot";
import { actionClient } from "@/lib/safe-action";

export const createUser = actionClient
	.inputSchema(
		v.object({
			name: v.pipe(v.string(), v.minLength(2)),
			email: v.pipe(v.string(), v.email()),
		})
	)
	.action(async ({ parsedInput }) => {
		// parsedInput: { name: string, email: string }
	});
src/app/actions.ts
"use server";

import { type } from "arktype";
import { actionClient } from "@/lib/safe-action";

export const createUser = actionClient
	.inputSchema(
		type({
			name: "string >= 2",
			email: "string.email",
		})
	)
	.action(async ({ parsedInput }) => {
		// parsedInput: { name: string, email: string }
	});

How it works

next-safe-action doesn't import or depend on any validation library directly. Instead, it uses the Standard Schema protocol:

  1. Your schema exposes a ~standard property with a validate method
  2. next-safe-action calls schema["~standard"].validate(input) at runtime
  3. The result is either { value } (success) or { issues } (failure)
  4. Type inference works through StandardSchemaV1.InferInput<S> and StandardSchemaV1.InferOutput<S>

This means:

  • Zero adapter code, validation libraries work directly
  • Full type inference, parsedInput and validation errors are typed
  • Runtime agnostic, any compliant library works identically

Choosing a library

FeatureZodValibotArkType
Bundle size~14 KB~1-5 KB (tree-shakeable)~30 KB
API styleMethod chainingFunctional / pipe-basedTypeScript syntax
EcosystemLargest (adapters, plugins)GrowingSmaller
PerformanceGoodGoodExcellent
Error messagesCustomizableCustomizableBuilt-in

All three libraries work identically with next-safe-action. Choose based on your project's needs: bundle size, API preference, or ecosystem requirements.

Mixing libraries

Since next-safe-action uses Standard Schema at the protocol level, you can even use different libraries for different actions in the same project:

// Action using Zod
export const createUser = actionClient
	.inputSchema(z.object({ name: z.string() }))
	.action(async ({ parsedInput }) => { /* ... */ });

// Action using Valibot (in the same project!)
export const updateSettings = actionClient
	.inputSchema(v.object({ theme: v.picklist(["light", "dark"]) }))
	.action(async ({ parsedInput }) => { /* ... */ });

This works because the action client only interacts with schemas through the Standard Schema interface. It doesn't know (or care) which library created the schema.

See also

On this page