VfContent
Basic Usage
VfContent renders static content inside the form — plain text, dynamic Expressions, raw HTML, or custom markup via its default slot — as the examples on this page demonstrate. It's a display-only element: it holds no value and adds nothing to the form data.
<VfContent name="intro" content="Please fill out the form below." /><template>
<VfContent name="intro" content="Please fill out the form below." />
</template><script setup lang="ts">
import { defineSchema, useForm } from '@vueform/ui'
const schema = defineSchema({
intro: {
type: 'content',
content: 'Please fill out the form below.',
},
})
const [Form] = useForm({ schema })
</script>
<template>
<Form />
</template>Dynamic Content
The content prop supports expressions. Wrap element names or calculations in {...} to create dynamic text that updates reactively.
<VfText name="name" label="Name" default="World" />
<VfContent name="greeting" content="Hello, {name}!" /><template>
<VfText name="name" label="Name" default="World" />
<VfContent name="greeting" content="Hello, {name}!" />
</template><script setup lang="ts">
import { defineSchema, useForm } from '@vueform/ui'
const schema = defineSchema({
name: {
type: 'text',
label: 'Name',
default: 'World',
},
greeting: {
type: 'content',
content: 'Hello, {name}!',
},
})
const [Form] = useForm({ schema })
</script>
<template>
<Form />
</template>HTML Content
Content containing markup renders as HTML by default — to restrict this or set up sanitization, see HTML sanitization.
<VfContent
name="notice"
content="This is <b>bold</b> and <em>italic</em> text."
/><template>
<VfContent
name="notice"
content="This is <b>bold</b> and <em>italic</em> text."
/>
</template><script setup lang="ts">
import { defineSchema, useForm } from '@vueform/ui'
const schema = defineSchema({
notice: {
type: 'content',
content: 'This is <b>bold</b> and <em>italic</em> text.',
},
})
const [Form] = useForm({ schema })
</script>
<template>
<Form />
</template>Slot
Use the default slot to render custom content instead of the content prop.
<VfContent name="notice">
<template #default>
<p>Please read our <a href="#">terms and conditions</a> before submitting.</p>
</template>
</VfContent><template>
<VfContent name="notice">
<template #default>
<p>Please read our <a href="#">terms and conditions</a> before submitting.</p>
</template>
</VfContent>
</template><script setup lang="ts">
import { defineSchema, defineSlot, useForm } from '@vueform/ui'
import { h } from 'vue'
const schema = defineSchema({
notice: {
type: 'content',
slots: {
default: defineSlot('VfContent.default', {
setup() {
return () => h('p', [
'Please read our ',
h('a', { href: '#' }, 'terms and conditions'),
' before submitting.',
])
},
}),
},
},
})
const [Form] = useForm({ schema })
</script>
<template>
<Form />
</template>Props
Configure the component's behavior and appearance with these props.
General
| Prop | Default | Description |
|---|---|---|
name | — | Required. The element's unique identifier and path in the current level of form data model. |
type | 'content' | The element type identifier. |
id | — | The HTML id attribute for the input element. |
Layout
| Prop | Default | Description |
|---|---|---|
label | — | The label displayed alongside the element. |
hideLabel | false | Whether to visually hide the element's label. |
content | — | The text content rendered inside the element. |
page | — | Page configuration for stepped/tabbed forms. Accepts a simple label or a metadata tuple with at least a label property. |
help | — | A help text displayed below the label. |
align | — | Horizontal alignment of the element within its layout container. |
columns | — | Responsive grid column sizing for the element's layout. |
layout | true | Custom layout template for this element, false to render the element without any layout wrapper, or true (default) for normal layout. |
spaceTop | — | Adds vertical spacing (padding) above the element. The value maps to a spacing token (1-3): 1 = var(--vf-space-1), .... |
spaceBottom | — | Adds vertical spacing (padding) above the element. The value maps to a spacing token (1-3): 1 = var(--vf-space-1), .... |
lineTop | false | Renders a horizontal line (border) above the element. |
lineBottom | false | Renders a horizontal line (border) below the element. |
Element
| Prop | Default | Description |
|---|---|---|
noExpressions | false | Disables expression resolution for this element. Applies to the element itself only (container elements don't cascade it to their children), and a form-level noExpressions cannot be overridden by setting it to false here. |
description | — | A description text displayed below the element. |
aligned | false | Whether the element should vertically align with adjacent form controls. |
Data
| Prop | Default | Description |
|---|---|---|
detached | false | Whether the element is detached from the form's element registry and data model (if applicable). |
Conditions
| Prop | Default | Description |
|---|---|---|
conditions | — | Conditions that control the element's visibility and availability. |
conditionsTrigger | — | The source that triggers re-evaluation of this element's async conditional props (conditions, disabled, readonly, and validation rule conditions). Accepts an array of element paths whose values are watched, a Ref, or a function used as a watch getter. When omitted, any change in the form model re-triggers evaluation. |
conditionsDebounce | — | The debounce delay in milliseconds applied before re-evaluating this element's async conditional props when the trigger source changes. Overrides the global $conditions.debounce config (default 300). |
Customization
| Prop | Default | Description |
|---|---|---|
parts | — | Custom DOM attribute overrides for the component's structural parts. |
class | — | CSS classes to apply to the component's root element. |
classes | — | Custom CSS class overrides for the component's structural parts. |
reclass | — | CSS class overrides that replace (rather than merge with) default classes. |
tokens | — | Token overrides applied to the element's CSS custom properties. |
slots | — | Custom Vue components to use for named slots. Every component resolves it through the standard outlets pipeline (see useOutlets). |
presets | — | Named configuration presets to apply to the component. |
template | — | A custom Vue component to use as the template instead of the default. |
component | — | A custom Vue component to render in the component's default slot. |
Context
Reactive properties and methods accessible on the component's Context object.
General
| Name | Description |
|---|---|
type | The element's type identifier (e.g. "text", "checkbox", "select"). |
name | The element's name as a string ref. |
props | The resolved component props with defaults applied. |
Content
| Name | Description |
|---|---|
isStatic | Whether the element is a static (non-data) element. |
description | The resolved description text for the element. |
descriptionId | The DOM id for the element's description text region. |
content | The resolved text content for the element — sanitized in the resolution pipeline after localization and expressions, and only when the resolved string actually contains markup, so plain text never enters the engine. |
isHtml | Whether the content renders as HTML. Markup renders as HTML out of the box; a configured $security.allowHtml must list the content area, and only the app config decides — no schema entry can grant itself HTML rendering. When refused, the markup renders as escaped text instead. Markup-free content never consults the gate — it renders identically either way. |
tag | The HTML tag used to render the content wrapper. |
State
| Name | Description |
|---|---|
visible | Whether the element is currently visible, based on availability, container visibility, and pager state. |
available | Whether the element is available (not hidden by conditional logic). |
Validation
| Name | Description |
|---|---|
errors | The reactive list of validation error messages. |
displayErrors | The reactive list of validation error messages. |
messages | The reactive list of informational messages. |
messageBag | The element's message bag holding user-facing error and message strings. |
genericName | A human-readable name resolved from displayName, label, floating, placeholder, or the element's name prop. |
Layout
| Name | Description |
|---|---|
id | The resolved element id, using the id prop or falling back to the auto-generated uid. |
label | The resolved label text for the element. |
help | The resolved help text for the element. |
page | Page metadata for stepped/tabbed forms. |
classes | The resolved CSS class map for the component. |
parts | The resolved DOM attribute overrides for internal elements. |
viewLoaded | Whether the element's view has been loaded (used for lazy rendering optimization). |
$el | Reference to the component's root DOM element. |
resolvedColumns | Resolved column sizes per breakpoint, normalized into container/label/wrapper/group values. |
updateColumns() | Updates the column configuration programmatically. |
scrollTo() | Scrolls the window to this element. If the element belongs to a step or tab, that page is selected first before scrolling. |
focus() | Focuses the first focusable HTML element (input, select, textarea, button, etc.) within the wrapper. Optionally scrolls to the element first. |
Tree
| Name | Description |
|---|---|
form$ | The parent form's context, providing access to form-level state and methods. |
path | The element's dot-notation path in the form tree (e.g. "user.name"). |
componentName | The resolved Vue component name for this element. |
container | The container context providing path, model, and registration methods for child elements. |
children | The child element registry providing registration, unregistration, and reactive child lists. |
Events
| Name | Description |
|---|---|
fire() | Fires an event synchronously, invoking before, main, and after listeners in order. |
fireAsync() | Fires an event asynchronously, awaiting each callback and short-circuiting when any returns false. |
on() | Subscribes a callback to a specific event. |
off() | Removes a previously registered event callback. |
onBefore() | Registers a callback that runs before the main event listeners. |
onAfter() | Registers a callback that runs after the main event listeners. |
Accessibility
| Name | Description |
|---|---|
uid | A unique auto-generated identifier for the element. |
helpId | The DOM id for the element's help text region. |
errorId | The DOM id for the element's error message region. |
labelId | The DOM id for the element's label region. |
Events
Events emitted by the component. See Events for details.
| Name | Params | Description |
|---|---|---|
beforeMount | context$ | Emitted before the component is mounted. |
mounted | context$ | Emitted after the component is mounted. |
beforeUnmount | context$ | Emitted before the component is unmounted. |
unmounted | context$ | Emitted after the component is unmounted. |
updated | context$ | Emitted after the component is updated. |
Slots
Named Slots for replacing parts of the component's rendering.
| Name | Scope | Description |
|---|---|---|
template | context | Slot that replaces the entire component rendering. Receives the full component context as a single context prop. |
label | label | Slot for customizing the element's label content. |
required | — | Slot for customizing the required indicator. |
help | help | Slot for customizing the help text. |
default | Context | Default slot that replaces the content. Receives the full component context. |
description | description | Slot for customizing the description text. |
errors | errors | Slot for customizing the validation errors display. |
messages | messages | Slot for customizing the info messages display. |
Parts
DOM attribute overrides targeting internal elements. See Parts for details.
panel
The wrapper around root with display: contents when the element is a step or a tab.
| Element | <div> |
| Classes | .vf-panel |
root
The element's root wrapper.
| Element | <div> <fieldset> |
| Classes | .vf-element .vf-space-top-1? .vf-space-top-2? .vf-space-top-3? .vf-space-bottom-1? .vf-space-bottom-2? .vf-space-bottom-3? .vf-line-top? .vf-line-bottom? .vf-content |
| Bindings | data-align-label data-align data-aligned |
labelGroup
The container holding the label and required indicator.
| Element | <div> |
| Classes | .vf-label-group |
label
The label element, associated with the control via for.
| Element | <label> <span> |
| Classes | .vf-label |
required
The required indicator.
| Element | <div> |
| Classes | .vf-required |
elementGroup
The container wrapping the input and related UI.
| Element | <div> |
| Classes | .vf-element-group |
elementWrapper
The wrapper directly around the input control.
| Element | <div> |
| Classes | .vf-element-wrapper |
help
The help text area.
| Element | <div> |
| Classes | .vf-help |
description
The description text area.
| Element | <div> |
| Classes | .vf-description |
errors
The validation errors container.
| Element | <div> |
| Classes | .vf-errors |
error
An individual validation error.
| Element | <div> |
| Classes | .vf-error |
messages
The info messages container.
| Element | <div> |
| Classes | .vf-messages |
message
An individual info message.
| Element | <div> |
| Classes | .vf-message |
Tokens
All the CSS variables that control this component's appearance with default values in the default theme. See Theming Guide for details.
:root {
/**
* Element tokens
* -------------------------------------------------------------------------- */
--vf-content-aligned-margin: calc(var(--vf-input-pt) + var(--vf-input-border-width-t)) 0 0 0;
/**
* Shared element layout tokens
* -------------------------------------------------------------------------- */
--vf-element-line-color: var(--vf-gray-300);
--vf-label-font-size: 1rem;
--vf-label-font-weight: 600;
--vf-label-group-h-padding: calc(var(--vf-py) + var(--vf-border-width-t)) 0 0 0;
--vf-label-group-v-padding: 0 0 0.375rem 0;
--vf-required-bg: transparent;
--vf-required-color: inherit;
--vf-required-content: '*';
--vf-required-h: auto;
--vf-required-icon: none;
--vf-required-w: auto;
--vf-help-bg: transparent;
--vf-help-border-color: transparent;
--vf-help-border-style: solid;
--vf-help-border-width: 0;
--vf-help-color: var(--vf-text-soft);
--vf-help-font-size: 0.875rem;
--vf-help-padding: 0;
--vf-help-radius: var(--vf-radius);
--vf-description-bg: transparent;
--vf-description-border-color: transparent;
--vf-description-border-style: solid;
--vf-description-border-width: 0;
--vf-description-color: var(--vf-text-soft);
--vf-description-font-size: 0.875rem;
--vf-description-padding: 0;
--vf-description-radius: var(--vf-radius);
--vf-errors-bg: transparent;
--vf-errors-border-color: transparent;
--vf-errors-border-style: solid;
--vf-errors-border-width: 0;
--vf-errors-color: var(--vf-red);
--vf-errors-font-size: 0.875rem;
--vf-errors-padding: 0;
--vf-errors-radius: var(--vf-radius);
--vf-messages-bg: transparent;
--vf-messages-border-color: var(--vf-border-success);
--vf-messages-border-style: solid;
--vf-messages-border-width: 0;
--vf-messages-color: var(--vf-green);
--vf-messages-font-size: 0.875rem;
--vf-messages-padding: 0;
--vf-messages-radius: var(--vf-radius);
--vf-input-border-width-l: var(--vf-border-width-l);
--vf-icon-font-size: 0.875rem;
--vf-icon-group-color: inherit;
--vf-icon-group-color-danger: var(--vf-color-danger);
--vf-icon-group-width: 2.5rem;
--vf-element-group-gutter-x: 0;
--vf-element-group-gutter-y: 0.25rem;
--vf-element-wrapper-padding: 0;
/**
* Semantic tokens
* -------------------------------------------------------------------------- */
--vf-icon-height: 0.75rem;
--vf-icon-width: 0.75rem;
/**
* Primitives tokens
* -------------------------------------------------------------------------- */
--vf-space-1: 1rem;
--vf-space-2: 2rem;
--vf-space-3: 3rem;
--vf-icon-loading: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.5' fill='none' stroke='currentColor' stroke-width='1.5' stroke-dasharray='30.6 10.2' stroke-linecap='round'/%3E%3C/svg%3E");
}{
/**
* Element tokens
* -------------------------------------------------------------------------- */
contentAlignedMargin: 'calc($inputPt + $inputBorderWidthT) 0 0 0',
/**
* Shared element layout tokens
* -------------------------------------------------------------------------- */
elementLineColor: '$gray300',
labelFontSize: '1rem',
labelFontWeight: '600',
labelGroupHPadding: 'calc($py + $borderWidthT) 0 0 0',
labelGroupVPadding: '0 0 0.375rem 0',
requiredBg: 'transparent',
requiredColor: 'inherit',
requiredContent: "'*'",
requiredH: 'auto',
requiredIcon: 'none',
requiredW: 'auto',
helpBg: 'transparent',
helpBorderColor: 'transparent',
helpBorderStyle: 'solid',
helpBorderWidth: '0',
helpColor: '$textSoft',
helpFontSize: '0.875rem',
helpPadding: '0',
helpRadius: '$radius',
descriptionBg: 'transparent',
descriptionBorderColor: 'transparent',
descriptionBorderStyle: 'solid',
descriptionBorderWidth: '0',
descriptionColor: '$textSoft',
descriptionFontSize: '0.875rem',
descriptionPadding: '0',
descriptionRadius: '$radius',
errorsBg: 'transparent',
errorsBorderColor: 'transparent',
errorsBorderStyle: 'solid',
errorsBorderWidth: '0',
errorsColor: '$red',
errorsFontSize: '0.875rem',
errorsPadding: '0',
errorsRadius: '$radius',
messagesBg: 'transparent',
messagesBorderColor: '$borderSuccess',
messagesBorderStyle: 'solid',
messagesBorderWidth: '0',
messagesColor: '$green',
messagesFontSize: '0.875rem',
messagesPadding: '0',
messagesRadius: '$radius',
inputBorderWidthL: '$borderWidthL',
iconFontSize: '0.875rem',
iconGroupColor: 'inherit',
iconGroupColorDanger: '$colorDanger',
iconGroupWidth: '2.5rem',
elementGroupGutterX: '0',
elementGroupGutterY: '0.25rem',
elementWrapperPadding: '0',
/**
* Semantic tokens
* -------------------------------------------------------------------------- */
iconHeight: '0.75rem',
iconWidth: '0.75rem',
/**
* Primitives tokens
* -------------------------------------------------------------------------- */
space1: '1rem',
space2: '2rem',
space3: '3rem',
iconLoading: "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.5' fill='none' stroke='currentColor' stroke-width='1.5' stroke-dasharray='30.6 10.2' stroke-linecap='round'/%3E%3C/svg%3E\")",
}