Zod 3 × Zod 4

svelte-number-format has no Zod dependency at all — bind:value hands you a plain number or raw string, so any validator (or none) works. Below, the same two bound values are validated live through both Zod majors at once.

Try an amount over $1,000,000, or clear the field, and watch both columns react.

Zod 3 import { z } from 'zod'

✓ valid

{
  "amount": 1500,
  "phone": "4155551234"
}

Zod 4 import { z } from 'zod/v4'

✓ valid

{
  "amount": 1500,
  "phone": "4155551234"
}

What actually differs

For the schemas these components need, the API is the same in both majors. The differences show up at the edges:

Schemas

// Identical in Zod 3 and Zod 4
import * as z from 'zod'          // Zod 3
import * as z from 'zod/v4'       // Zod 4 API from the zod@3.25+ package
import * as z from 'zod'          // or zod@4 installed directly

const schema = z.object({
  amount: z.number().min(0).max(1_000_000),
  phone: z.string().regex(/^\d{10}$/, 'Must be exactly 10 digits')
})

Custom error messages

// Custom error params were renamed
z3.number().max(1_000_000, { message: 'Must be ≤ 1,000,000' })  // Zod 3
z4.number().max(1_000_000, { error: 'Must be ≤ 1,000,000' })    // Zod 4
z.number().max(1_000_000, 'Must be ≤ 1,000,000')                // both

Superforms & Formsnap

// sveltekit-superforms ships an adapter per major
import { zodClient } from 'sveltekit-superforms/adapters'   // Zod 3
import { zod4Client } from 'sveltekit-superforms/adapters'  // Zod 4

Felte

@felte/validator-zod declares a zod ^3 peer dependency, so the Felte demo sticks with Zod 3.

SvelteKit remote form functions

// SvelteKit remote form functions accept any Standard
// Schema validator — Zod 3.24+ and Zod 4 both qualify
export const postForm = form(
  z.object({ amount: z.number().min(0) }),
  async (data) => { /* data.amount is a number */ }
)