Skip to content

Fields

Fields define the editable properties of blocks. They control what users can edit in the sidebar editor.

Fields and Themes

Fields are organized within themes in two ways:

  1. Block fields - Defined in fields.ts files within block directories (theme-specific)
  2. Custom field components - Located in a theme's fields/ directory (theme-specific)

Since blocks are theme-specific, their fields are automatically scoped to their theme. Custom field components are also theme-specific and can be shared across blocks within the same theme.

Creating Block Fields

Fields are defined in a fields.ts file within your block directory:

typescript
// src/page-builder/default/blocks/hero/fields.ts
import { createField } from "vue-wswg-editor";

export default {
   heading: createField({
      type: "text",
      label: "Heading",
      required: true,
      default: "Welcome",
   }),
   description: createField({
      type: "textarea",
      label: "Description",
      rows: 4,
   }),
};

Custom Field Components

Themes can include custom field components in a fields/ directory:

src/page-builder/
  default/
    fields/
      rich-text/
        RichTextField.vue
        RichTextToolbar.vue
    blocks/
      hero/
        Hero.vue
        fields.ts  # Can use custom fields from theme's fields/ directory

Custom field components can be used in block fields using the custom field type. See the Custom Fields Guide for more information.

Supported Field Types

  • text - Single-line text input
  • textarea - Multi-line text input
  • number - Number input with min/max/step
  • boolean - Checkbox
  • email - Email input with validation
  • url - URL input with validation
  • select - Dropdown select
  • checkbox - Checkbox group
  • radio - Radio button group
  • color - Color picker
  • range - Range slider with optional value suffix
  • image - Image upload with preview and drag & drop
    • ⚠️ Not recommended for production - uses base64 encoding (read more)
  • repeater - Repeating field groups
  • object - Nested object fields for targeting nested data (e.g., details.title, details.image)
  • margin - Margin configuration (top/bottom)
  • info - Read-only information display
  • custom - Custom field component

Field Options

OptionTypeDescription
typeEditorFieldTypeField type (required)
labelstringField label
descriptionstringHelp text below field
placeholderstringInput placeholder
requiredbooleanWhether field is required
defaultanyDefault value
hiddenbooleanHide field from editor
groupstringGroup field in sidebar
optionsArray<{label, value, id}>Options for select/radio/checkbox
min / maxnumberMin/max for number/range
stepnumberStep for number/range
rowsnumberRows for textarea
validatorValidatorFunctionCustom validation function
componentComponentCustom component for custom type
valueSuffixstringSuffix to display after range value (e.g., "px", "%")
responsivebooleanWhether image is responsive

Validation

Fields support both built-in validations (based on field properties) and custom validator functions. Both types run automatically, with built-in validations running first.

Built-in Validations

Built-in validations are configured using field properties:

typescript
export default {
   // Required field
   title: createField.text({
      label: "Title",
      required: true,
   }),

   // String length validation
   description: createField.textarea({
      label: "Description",
      minLength: 10,
      maxLength: 500,
   }),

   // Number range validation
   count: createField.number({
      label: "Count",
      min: 0,
      max: 100,
   }),

   // Repeater item count validation
   items: createField.repeater(
      { name: createField.text({ label: "Name" }) },
      {
         label: "Items",
         minItems: 1,
         maxItems: 10,
      }
   ),
};

Custom Validators

Custom validators allow complex validation logic:

typescript
export default {
   text: createField.text({
      label: "Announcement Text",
      maxLength: 50, // Built-in validation
      required: true, // Built-in validation
      validator: async (value) => {
         // Custom validation runs after built-in validations
         if (value && value.length >= 50) {
            return "Text must be less than 50 characters";
         }
         if (value && value.includes("spam")) {
            return "Text cannot contain the word 'spam'";
         }
         return true;
      },
   }),
};

Validation Properties

PropertyTypeDescriptionField Types
requiredbooleanWhether field is requiredAll
minLengthnumberMinimum string lengthtext, textarea, email, url
maxLengthnumberMaximum string lengthtext, textarea, email, url
minnumberMinimum numeric valuenumber, range
maxnumberMaximum numeric valuenumber, range
minItemsnumberMinimum number of repeater itemsrepeater
maxItemsnumberMaximum number of repeater itemsrepeater
validatorValidatorFunctionCustom validation functionAll

For detailed validation examples, see the Validation Guide.

Field Examples

Image Field Example

typescript
export default {
   logo: createField.image({
      label: "Logo",
      required: true,
      description: "Upload your company logo",
      responsive: false,
   }),
};

The image field provides a compact upload interface with:

  • Thumbnail preview (64x64px)
  • Drag & drop support
  • Click to upload/replace
  • Remove image functionality
  • Returns a URL string after upload

Range Field with Suffix Example

typescript
export default {
   logoWidth: createField.range({
      label: "Logo Width",
      min: 50,
      max: 300,
      step: 5,
      default: 144,
      valueSuffix: "px", // Displays "144px" in the slider
   }),
};

Object Field Example

The object field variant allows you to target nested object data, such as details.title and details.image. This is useful when your block data structure contains nested objects.

typescript
export default {
   details: createField.object(
      {
         title: createField.text({
            label: "Title",
            required: true,
            placeholder: "Enter title",
         }),
         image: createField.image({
            label: "Image",
            description: "Upload an image",
         }),
         description: createField.textarea({
            label: "Description",
            rows: 3,
         }),
      },
      {
         label: "Details",
         description: "Configure the details section",
      }
   ),
};

In your block component, you can access these nested properties:

vue
<template>
   <div>
      <h2>{{ block.details?.title }}</h2>
      <img v-if="block.details?.image" :src="block.details.image" alt="" />
      <p>{{ block.details?.description }}</p>
   </div>
</template>

The object field creates a nested structure in your block data:

json
{
   "details": {
      "title": "My Title",
      "image": "https://example.com/image.jpg",
      "description": "My description"
   }
}

See the API Reference for detailed examples of each field type.

Released under the MIT License.