Rendering Modes
Enforma provides multiple rendering approaches to accommodate different developer preferences and application requirements.
Enforma supports four main rendering modes:
- Field-based Rendering - Explicit component declarations with maximum control
- Schema-based Rendering - JSON-driven form generation with minimal markup
- Headless Mode - Pure behavior with complete UI control
- Mixed Mode - Combining approaches for edge-cases
Field-based Rendering
📔 Complete working example here
This approach uses explicit component declarations for each form element:
vue
<template>
<Enforma :data="data":rules="rules" :submitHandler="submit">
<EnformaField
name="firstName"
label="First Name"
... rest of the props go here
/>
<EnformaField name="lastName" label="Last Name" />
<EnformaField name="email" label="Email Address" />
<EnformaSubmitButton>Submit</EnformaSubmitButton>
</Enforma>
</template>Benefits:
- Complete control over field placement and layout
- Direct access to field components for customization
- Familiar template-based approach
- Optional use of custom components (eg: your own submit button)
Schema-based Rendering
📔 Complete working example here
vue
<template>
<Enforma :data="data" :rules="rules" :schema="formSchema" :submitHandler="submit" />
</template>
<script setup>
const formSchema = {
first_name: {
label: 'First name',
required: true, // mark field as required
inputProps: {
// custom props for the input component
}
}
};
</script>Benefits:
- Concise form definitions
- Easy dynamic form generation
- Separation of form structure from presentation
- Excellent for server-driven forms
You can learn more about using schemas in the Schema Forms section
Headless Mode
📔 Complete working example here
This approach provides behavior without enforcing design:
vue
<template>
<HeadlessForm :data="data":rules="rules" :submitHandler="submit">
<template #default="formCtrl">
<HeadlessField name="firstName">
<template #default="{ id, attrs, events, error}">
<div>
<label :for="id">First Name</label>
<input v-bind="attrs" v-on="events" />
<<div
v-if="error"
:id="attrs['aria-errormessage']">
{{ error }}
</div>
</div>
</template>
</HeadlessField>
<!-- Other fields -->
<button type="submit">Submit</button>
</template>
</HeadlessForm>
</template>Benefits:
- Complete UI design freedom
- Full access to form state and behavior
- Works with any UI framework or custom components
When using Headless components it's best to create an app-specific wrapper component
Mixed Mode
📔 Complete working example here
vue
<template>
<Enforma :data="data" :rules="rules" :schema="schema" :submitHandler="submit">
<!-- passing just the name enough if `email` is defined in the schema -->
<EnformaField name="email" />
<!-- use a field that is missing from the schema -->
<EnformaField name="phone" label="Phone" />
<!-- use a headless component if the default rendering result is not to your liking -->
<HeadlessField name="password">
<!-- template goes here -->
</HeadlessField>
<EnformaSubmitButton>Submit</EnformaSubmitButton>
</Enforma>
</template>Using headless fields along side base components should be reserved for prototyping. Once you know what you want you should create your own components.
