---
title: @storyblok/schema
description: @storyblok/schema is Storyblok's shared schema layer for authoring blocks, fields, folders, datasources, and field plugins as typed TypeScript objects.
url: https://www.storyblok.com/docs/libraries/js/schema
---

# @storyblok/schema

[@storyblok/schema](https://github.com/storyblok/monoblok/tree/main/packages/schema) is Storyblok’s shared schema layer. It exports TypeScript types and `define*` helpers for authoring Storyblok content shapes (blocks, fields, folders, datasources, and field plugins) in code. The resulting schema is consumed at runtime by the `storyblok schema` [CLI commands](/docs/libraries/storyblok-cli#schema-init) and at compile time for narrowing API client responses.

> [!WARNING]
> `@storyblok/schema` is currently published under the `@alpha` dist-tag. No breaking changes are planned before the stable `1.0.0` release.

## Requirements

-   **Node.js** LTS (version 22.x recommended)

## Installation

Add the package to a project by running this command in the terminal:

```bash
npm install @storyblok/schema@alpha
```

## Usage

Define a schema with the `define*` helpers exported by this package, then use the `storyblok schema` [CLI commands](/docs/libraries/storyblok-cli#schema-init) to push it to a Storyblok space. The package exposes typed factories for blocks, fields, folders, datasources, and field plugins, plus a `defineSchema()` wrapper that ties them together.

Each `define*` helper is a thin, strongly-typed identity function: it preserves the literal types of its input so downstream helpers and the `Schema` type can narrow the content shape. The helpers do not transform payloads into the Management API wire format, the CLI owns that mapping.

### Basic example

A minimal schema declaration with `@storyblok/schema`:

```ts
import { defineBlock, defineDatasource, defineField, defineFolder, defineSchema, type Schema as InferSchema } from "@storyblok/schema";

const layoutFolder = defineFolder({ name: "Layout" });

const heroBlock = defineBlock({
  name: "hero",
  is_nestable: true,
  folder: layoutFolder,
  fields: [defineField("headline", { type: "text", required: true }), defineField("image", { type: "asset", filetypes: ["images"] })],
});

const colorsDatasource = defineDatasource({ name: "Colors", slug: "colors" });

export const schema = defineSchema({
  blocks: { heroBlock },
  folders: { layoutFolder },
  datasources: { colorsDatasource },
});

export type Schema = InferSchema<typeof schema>;
```

## API

The following sections document each `define*` helper, validator, and type export.

### `defineBlock()`

```ts
defineBlock(block);
```

Returns a typed block (component) definition. The helper preserves the literal types in `name` and `fields` so the `Schema` type can narrow the content shape. Fields are passed as an ordered array under `fields`; the array index becomes each field’s `pos`. The helper throws on duplicate field names.

```ts
import { defineBlock, defineField } from "@storyblok/schema";

export const pageBlock = defineBlock({
  name: "page",
  is_root: true,
  fields: [defineField("title", { type: "text", required: true }), defineField("body", { type: "bloks" })],
});
```

`defineBlock()` accepts every property exposed by Storyblok’s [component MAPI schema](/docs/api/management/components/the-component-object). The most common properties:

| Property | Description |
| --- | --- |
| `name` | The technical name of the block. |
| `fields` | _Required._ An ordered array of field definitions produced by `defineField()`. |
| `display_name` | Optional human-readable name shown in the Storyblok UI. |
| `is_root` | Marks the block as a content type (root block). Defaults to `false`. |
| `is_nestable` | Marks the block as nestable inside `bloks` fields. Defaults to `true`. |
| `folder` | The block folder this block belongs to. Accepts a `defineFolder()` result, a path string, or `null`. |
| `component_group_uuid` | _Deprecated._ Low-level escape hatch for assigning a block to a group by UUID. Use `folder` instead. Setting both throws. |

### `defineField()`

```ts
defineField(name, definition);
```

Returns a typed field definition stamped with its `name`. The `type` discriminates the accepted properties. For example, an `asset` field accepts `filetypes`, while a `text` field accepts `max_length`.

```ts
import { defineField } from "@storyblok/schema";

const headlineField = defineField("headline", { type: "text", max_length: 120, required: true });
const imageField = defineField("image", { type: "asset", filetypes: ["images"] });
```

In addition to the wire-shape field properties, `defineField()` accepts three DSL reference keys that are normalized to plain strings in the result:

| Key | Description |
| --- | --- |
| `required` | Marks the field as required. |
| `allow` | Restricts a `bloks` field to specific blocks or folders. Accepts a `defineBlock()` result, a block name, a `defineFolder()` result, or an array thereof. A single `allow` list restricts by either blocks or folders, not both. |
| `datasource` | Associates an `option`/`options` field with a datasource. Accepts a `defineDatasource()` result or a datasource slug. |

```ts
import { defineField } from "@storyblok/schema";

const bodyField = defineField("body", { type: "bloks", allow: [heroBlock, "teaser"] });
const themeField = defineField("theme", { type: "option", source: "internal", datasource: colorsDatasource });
```

Hoist field definitions into reusable variables and reference them from multiple block schemas to maintain a single source of truth for shared fields such as `seo_title` or `body`.

The package also exports type-level helpers for working with fields and their values: `Field`, `FieldInput`, `DefinedField`, `FieldType`, `FieldValue`, `FieldValueInput`, `AssetFieldValue`, `BlocksFieldValue`, `MultilinkFieldValue`, `PluginFieldValue`, `RichtextFieldValue`, `TableFieldValue`, `BlockContent`, and `BlockContentInput`.

### `defineFolder()`

```ts
defineFolder(folder);
```

Returns a typed block folder (component group) definition. A folder is identified by its resolved `path`, computed eagerly from the `parent` chain (display names joined with `/`). There is no UUID: the CLI matches or creates groups by path at push time. The helper throws when the name is empty or contains a `/`.

```ts
import { defineFolder } from "@storyblok/schema";

const layout = defineFolder({ name: "Layout" });
const heros = defineFolder({ name: "Heros", parent: layout });
// heros.path === "Layout/Heros"
```

Pass a folder to a block’s `folder` property, or to a field’s `allow` list, to reference it. The exported `BlockFolder` type describes the returned shape.

### `defineDatasource()`

```ts
defineDatasource(datasource);
```

Declares a datasource by `name` and `slug`, including optional `dimensions` for multi-dimensional datasources. Datasource entry values are content authored by editors, so they are not part of the schema and are neither modeled nor pushed here.

```ts
import { defineDatasource } from "@storyblok/schema";

export const colorsDatasource = defineDatasource({
  name: "Colors",
  slug: "colors",
  dimensions: [
    { name: "Light Mode", entry_value: "light" },
    { name: "Dark Mode", entry_value: "dark" },
  ],
});
```

The matching exported type is `Datasource`.

### `defineFieldPlugin()`

```ts
defineFieldPlugin(config);
```

Binds a Storyblok custom field `field_type` to a [Standard Schema](https://standardschema.dev) validator. Both the `fieldType` literal and the value type are inferred from the single argument. The `value` accepts any Standard Schema validator (Zod, Valibot, ArkType, or hand-written) and is retained for runtime use.

```ts
import { defineFieldPlugin } from "@storyblok/schema";
import { z } from "zod";

const colorPicker = defineFieldPlugin({
  fieldType: "my-custom-color-picker",
  value: z.object({ color: z.string(), alpha: z.number() }),
});
```

Register field plugins under a schema’s `fieldPlugins` key so the `Schema` type maps each `fieldType` to its validator output. The matching exported type is `FieldPlugin`.

Storyblok also ships official field plugins under the `@storyblok/schema/field-plugins` subpath. Import them individually so unused plugins tree-shake away:

```ts
import { storyblokColorField } from "@storyblok/schema/field-plugins";
```

`storyblokColorField` covers Storyblok’s official Colorpicker field (`storyblok-colorpicker`); its value type is exported as `StoryblokColorFieldValue`.

### `defineSchema()`

```ts
defineSchema(schema);
```

Typed identity wrapper for a schema object, consistent with the other `define*` helpers. It preserves the exact literal shape via `const` inference so the `Schema` type can derive block, datasource, and field-plugin types from `typeof schema`. It does not validate or transform.

```ts
import { defineSchema } from "@storyblok/schema";

export const schema = defineSchema({
  blocks: { pageBlock, heroBlock },
  folders: { layout },
  datasources: { colorsDatasource },
  fieldPlugins: { storyblokColorField },
});
```

The config accepts `blocks` (required) plus optional `folders`, `datasources`, and `fieldPlugins`, each a record of the corresponding `define*` results.

### `Schema`

```ts
type Schema<T>;
```

Derives a schema-types interface from the `typeof` a schema object. The resulting type exposes `blocks`, `datasources`, and `fieldPlugins`: `blocks` and `datasources` become unions of the corresponding `define*` results, and `fieldPlugins` becomes a `fieldType → validator output` map. Component groups are a UI concern and are not part of the derived types.

```ts
import { defineSchema } from "@storyblok/schema";
import type { Schema as InferSchema } from "@storyblok/schema";

export const schema = defineSchema({
  blocks: { pageBlock, heroBlock },
  datasources: { colorsDatasource },
  fieldPlugins: { storyblokColorField },
});

export type Schema = InferSchema<typeof schema>;
export type Blocks = Schema["blocks"];
export type Datasources = Schema["datasources"];
export type FieldPlugins = Schema["fieldPlugins"];
```

Passing the resulting `Schema` to `createApiClient(...).withTypes<Schema>()` narrows `cdn/stories/*` responses to the union of block content types declared in the schema.

### Validators

The package ships Zod-powered, non-throwing validators for checking a schema or a story against it:

```ts
validateSchema(schema);
validateStory(story, schema);
createStoryValidator(rootBlock, schema);
```

-   `validateSchema(schema)` returns a `ValidationResult` describing structural problems in the schema itself.
-   `validateStory(story, schema)` returns a `ValidationResult` describing whether a story’s content conforms to the schema.
-   `createStoryValidator(rootBlock, schema)` wraps `validateStory()` as a [Standard Schema](https://standardschema.dev) validator bound to a root block, so a story composes with any Standard Schema tooling (for example, as a tRPC input or a form resolver).

```ts
import { validateStory } from "@storyblok/schema";
import { schema } from "./schema";

const result = validateStory(story, schema);
if (!result.ok) {
  console.error(result.issues);
}
```

A `ValidationResult` has an `ok` boolean (`true` when there are no `error`\-severity issues) and an `issues` array. Each `ValidationIssue` carries a `severity` (`error` or `warning`), a machine-readable `code`, a `path`, an `entity`, and a human-readable `message`. The related types `ValidationResult`, `ValidationIssue`, and `ValidationSeverity` are exported.

## Further resources

[CLI schema commands](/docs/libraries/storyblok-cli#schema-init)Use schema init, schema push, and schema rollback to sync schemas with a Storyblok space.

## Pagination

-   [Previous: Migration Utilities](/docs/libraries/js/migrations)
-   [Next: Experiments](/docs/libraries/js/experiments)
