Nuxt

Component Index

To make Vue components available in the Drupal Canvas editor, the frontend exposes a component index: a JSON file listing all available components together with their props, prop schemas, labels, descriptions, categories, formats and schema references. Canvas External JS Components reads this index and uses it to populate the editor UI.

Component Index

To make Vue components available in the Drupal Canvas editor, the frontend exposes a component index: a JSON file listing all available components together with their props, prop schemas, labels, descriptions, categories, formats and schema references. Canvas External JS Components reads this index and uses it to populate the editor UI.

The nuxt-component-preview Nuxt module generates this index automatically from your components, picking up extra metadata from JSDoc annotations (implemented in src/runtime/server/utils/). The same module also powers the Component Previews used by the Canvas editor.

The index is served at /nuxt-component-preview/component-index.json.

Only global components are included. Components in components/global/ are automatically global; others can be registered with global: true in nuxt.config.ts.

Grouping Components by Folder

A recommended pattern is to keep Canvas components in a dedicated components/Canvas/ folder and derive categories from subfolder names:

components: [
  { path: '~/components/Canvas', global: true, pathPrefix: false, prefix: '' },
  { path: '~/components/global', global: true },
  '~/components',
],

componentPreview: {
  componentIndex: {
    // Derive Canvas component categories from folder names (Base/, Layout/, Card/, Hero/)
    category: { directory: true, fallback: 'Misc' },
  },
},

For working examples, see the Canvas components in the Lupus Decoupled Nuxt Starter.

JSDoc Metadata

Components and their props are documented via TypeScript, with JSDoc annotations supplying the additional metadata that TypeScript alone cannot express. The pattern is consistent: a label (display name) and an optional description, plus additional tags.

Component Metadata

Define component-level metadata via a JSDoc comment at the top of <script setup>:

<script setup lang="ts">
/**
 * Hero Billboard
 * @description A full-width hero section with background image and overlay.
 * @category Hero
 * @status stable
 */
</script>
TagDescription
First lineLabel / display name (falls back to auto-generated from PascalCase)
@descriptionComponent description shown in the editor
@categoryCategory override (alternative to directory-based)
@statusexperimental, stable, deprecated, or obsolete

All fields are optional. Config-level overrides take priority over JSDoc.

Prop Metadata

Props follow the same pattern — a label and description, plus additional tags for editor behavior:

<script setup lang="ts">
withDefaults(defineProps<{
  /**
   * Button label text
   * @example Submit
   */
  label?: string
  /**
   * Button variant
   * @enumLabels {"large": "Extra Large (XL)"}
   */
  variant?: 'primary' | 'secondary' | 'large'
}>(), {
  label: 'Click me',
  variant: 'primary'
})
</script>

The prop label is auto-generated from the first JSDoc line or prop name. Use @title to override.

TagDescription
@titleExplicit label override
@exampleAdds example values (multiple allowed)
@enumLabelsCustom labels for enum values, e.g. {"large": "Extra Large (XL)"}
@contentMediaType text/htmlEnables rich text editing in Canvas for string props
@formattingContext block|inlineControls formatting context (default: block)
@schemaRefReference Canvas JSON schema definitions (e.g. canvas/stream-wrapper-uri); array variant: @itemsSchemaRef
@formatJSON Schema format for semantic validation and UI widgets: date, date-time, time, duration, email, hostname, ipv4, ipv6, uuid, uri, uri-reference, etc.; array variant: @itemsFormat
@patternJSON Schema regex pattern for string validation
@allowed-schemesAllowed URI schemes for Canvas field type detection (e.g. public or http, https)
@minItems / @maxItemsCardinality bounds for array props (see Multi-Value Props)

Drupal Canvas Types

For Canvas integration, special TypeScript types are available that generate JSON schemas enabling Canvas UI features like media library selection. These types are auto-imported by Nuxt.

TypeDescription
CanvasImageImage with media library integration (src, alt, width, height)
CanvasVideoVideo with poster support

Example usage:

<script setup lang="ts">
/**
 * Hero Banner
 */
defineProps<{
  /**
   * Background image
   * @example src=https://example.com/hero.jpg alt="Hero" width=1200 height=600
   */
  image: CanvasImage
}>()
</script>

The @example for Canvas types supports two formats:

  • Key-value: src=https://... alt="text" width=800 height=600
  • JS object: { src: 'https://...', alt: 'text', width: 800 }

Schema References

For advanced use cases, @schemaRef allows referencing Canvas JSON schema definitions directly, useful for types like stream-wrapper-uri and stream-wrapper-image-uri. Use the shorthand prefix/name notation:

/**
 * @schemaRef canvas/stream-wrapper-uri
 */
imageUri?: string

This expands to json-schema-definitions://canvas.module/stream-wrapper-uri.

Multi-Value (Array) Props

Canvas 1.4 introduced support for multi-value props — arrays of primitives, formatted strings, enums, or known struct shapes. Declare them as TS array types; refinements TypeScript can't express are picked up via JSDoc.

Cardinality

TagEffect
@minItems NRequired-array — canvas treats this as the multi-value equivalent of required
@maxItems NCaps the number of entries

Element-shape refinements (applied to items)

TagEffect
@itemsFormat <fmt>Lifts format into items (e.g. uri, uri-reference, date, date-time, email)
@itemsSchemaRef <prefix/name>Lifts $ref into items, including known-shape extras (format, x-allowed-schemes, contentMediaType) — same as @schemaRef for scalar props

Enums in items

TypeScript union literals on the element type lift into items.enum + auto-generated items.meta:enum labels. @enumLabels on the prop overrides those labels.

<script setup lang="ts">
defineProps<{
  // Element labels auto-generated from values.
  status?: ('option_one' | 'option_two' | 'option_three')[]

  /**
   * @enumLabels {"draft": "Draft", "review": "In Review", "live": "Live"}
   */
  workflowState?: ('draft' | 'review' | 'live')[]

  // Numeric union → items.type: integer + enum.
  ratings?: (1 | 2 | 3 | 4 | 5)[]
}>()
</script>

Full example

<script setup lang="ts">
withDefaults(defineProps<{
  /**
   * Plain text values
   * @example ["Hello World", "Sample Text"]
   */
  textValues?: string[]

  /**
   * Required text values (canvas signals required via minItems)
   * @minItems 1
   */
  requiredText: string[]

  /**
   * Absolute URLs, capped at 3
   * @itemsFormat uri
   * @maxItems 3
   */
  links?: string[]

  /**
   * Event dates
   * @itemsFormat date
   */
  dates?: string[]

  /**
   * Workflow state list — labels overridden via @enumLabels
   * @enumLabels {"draft": "Draft", "review": "In Review", "live": "Live"}
   * @maxItems 3
   */
  states?: ('draft' | 'review' | 'live')[]

  /**
   * Public file attachments
   * @itemsSchemaRef canvas/stream-wrapper-uri
   */
  attachments?: string[]

  /**
   * Gallery images
   * @maxItems 5
   */
  images?: CanvasImage[]
}>(), {})
</script>

A complete example covering every shape combination can be seen in TestMultiValueProps.vue and its generated component-index.