Skip to content
Content
Skill

/page-settings

Extending the Website Builder page settings with custom settings groups and modifiers. Use this skill when the developer wants to add a new tab/group to the page settings drawer (e.g., Publishing, Analytics, Access Control), or modify an existing settings group (e.g., add fields

BOOST
From plugin
webiny-js
8k76 skills3 MCP
Install
$ npx -y skills add webiny/webiny-js --skill page-settings --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/page-settings

Context preview

The summary Claude sees to decide when to auto-load this skill.

Extending the Website Builder page settings with custom settings groups and modifiers. Use this skill when the developer wants to add a new tab/group to the page settings drawer (e.g., Publishing, Analytics, Access Control), or modify an existing settings group (e.g., add fields

SKILL.md

page-settings.SKILL.md
name: webiny-page-settings-extensions
description: >
  Extending the Website Builder page settings with custom settings groups and modifiers.
  Use this skill when the developer wants to add a new tab/group to the page settings
  drawer (e.g., Publishing, Analytics, Access Control), or modify an existing settings
  group (e.g., add fields to General or SEO). Covers PageSettingsGroup, PageSettingsGroupModifier,
  and the doc.extensions data model. For field types, renderers, and layout details,
  see the webiny-form-model skill.

Page Settings Extensions

TL;DR

Page settings extensions let you add new tabs to the page settings drawer or inject fields into existing tabs. Create a class implementing `PageSettingsGroup.Interface` for a new tab, or `PageSettingsGroupModifier.Interface` to extend an existing one. Register both via `createFeature()` and `<RegisterFeature>`. **Always store custom data in `doc.extensions` — never write to `doc.properties`, which is reserved for built-in system properties.**

**YOU MUST include the full file path with the `.tsx` extension in every `src` prop.** For example, use `src={"/extensions/myPageSettings/index.tsx"}`, NOT `src={"/extensions/myPageSettings/index"}`. Omitting the file extension will cause a build failure.

For field types, renderers, layout, validation, and all other form builder APIs, refer to the **webiny-form-model** skill.

Important: Where to Store Data

> **Use `doc.extensions` for all custom data.** The `doc.properties` object holds built-in system properties (title, path, snippet, image, tags, seo, social). Writing custom fields into `doc.properties` risks naming collisions with future Webiny updates and can corrupt system behavior. Always namespace your data under `doc.extensions.<yourGroupName>`.

// CORRECT — custom data in doc.extensions
mapFromForm(formData, doc) {
    doc.extensions.mySettings = doc.extensions.mySettings ?? {};
    doc.extensions.mySettings.myField = formData.myField;
}

// WRONG — never write custom data into doc.properties
mapFromForm(formData, doc) {
    doc.properties.myField = formData.myField; // DON'T DO THIS
}

Adding a New Settings Group

A new settings group appears as its own tab in the page settings drawer. Implement `PageSettingsGroup.Interface` with these members:

| Member | Type | Description | | ---------------------------- | ------------------------------------------- | --------------------------------------------------------- | | `name` | `string` | Unique group identifier (used as form field namespace) | | `label` | `string` | Tab label shown in the UI | | `description` | `string` (optional) | Description shown below the tab label | | `icon` | `{ type: "icon", name: string }` (optional) | FontAwesome icon for the tab (e.g., `"fas/calendar-alt"`) | | `buildForm(form)` | method | Define fields and layout | | `mapToForm(doc)` | method | Read from document to populate the form | | `mapFromForm(formData, doc)` | method | Write form values back to the document |

Complete Example: Publishing Settings Group

// extensions/myPageSettings/PublishingSettingsGroup.ts
import { PageSettingsGroup } from "webiny/admin/website-builder/page/editor";

class PublishingSettingsGroupImpl implements PageSettingsGroup.Interface {
  name = "publishing";
  label = "Publishing";
  description = "Configure publishing schedule and visibility.";
  icon = { type: "icon", name: "fas/calendar-alt" };

  buildForm(form: PageSettingsGroup.FormBuilder): void {
    form.fields(fields => ({
      publishDate: fields.datetime().withTimezone().label("Publish date"),
      unpublishDate: fields.datetime().dateOnly().label("Unpublish date"),
      visibility: fields
        .text()
        .label("Visibility")
        .options([
          { label: "Public", value: "public" },
          { label: "Private", value: "private" },
          { label: "Password Protected", value: "password" }
        ])
        .defaultValue("public"),
      featured: fields.boolean().label("Featured page")
    }));

    form.layout(layout => [
      layout.row("publishDate"),
      layout.row("unpublishDate"),
      layout.row("visibility"),
      layout.row("featured")
    ]);
  }

  mapToForm(doc: PageSettingsGroup.PageDocument): Record<string, any> {
    const publishing = doc.extensions?.publishing;
    return {
      publishDate: publishing?.publishDate ?? null,
      unpublishDate: publishing?.unpublishDate ?? null,
      visibility: publishing?.visibility ?? "public",
      featured: publishing?.featured ?? false
    };
  }

  mapFromForm(formData: Record<string, any>, doc: PageSettingsGroup.PageDocument): void {
    doc.extensions.publishing = doc.extensions.publishing ?? {};
    doc.extensions.publishing.publishDate = formData.publishDate;
    doc.extensions.publishing.unpublishDate = formData.unpublishDate;
    doc.extensions.publishing.visibility = formData.visibility;
    doc.extensions.publishing.featured = formData.featured;
  }
}

export const PublishingSettingsGroup = PageSettingsGroup.createImplementation({
  implementation: PublishingSettingsGroupImpl,
  dependencies: []
});

Modifying an Existing Settings Group

A modifier injects fields into an existing tab without subclassing it. Implement `PageSettingsGroupModifier.Interface`:

| Member | Type | Description

Read more
Ships withwebiny-js

Open-source content platform. Self-hosted on AWS serverless. Built as a TypeScript framework you extend with code, not a closed product you configure through a UI. Runs on Lambda, DynamoDB, S3, and CloudFront inside your own AWS account. Scales automatically.

Get the whole plugin
Stats
8,049
Stars
682
Forks
Active
Maintenance
TypeScript
Language
1d ago
Last commit
8y ago
Created
3d ago
Added

Repo: webiny/webiny-js

Other skills on webiny-js.