--- title: Internationalization (i18n) description: A guide in my new Starlight docs site. ---
# Internationalization (i18n)
Formwerk composables treat internationalization as a first class concern. It's not just about translating the labels and messages. It's mainly about catering to specifics of each locale audience and giving them the best experience possible.
If you didn't work with internationalization before, you might be surprised how the web still misses the mark in many cases. Here are a few examples:
- The `input[type="number"]` only accepts latin numerals. This causes issues for those with non-latin locales and keyboard layouts.
- Some keyboard layouts lack the semantically correct commas, and decimal separators, yet users use identical symbols that usually fails validation.
- Most keyboard shortcuts, especially left/right arrow keys do not respect the directionality of the locale.
- The HTML5 constrained validation API generates messages in the browser's default language, not the website's.
Formwerk composables take all of this into account and more. In this page we'll go over the details of how Formwerk handles internationalization and how you can benefit from it.
## Not an i18n library
Formwerk is not an i18n library. It does not provide a way to translate the labels and messages. But it does its best to offer the best experience it can with Vue's ecosystem.
You can use a dedicated i18n library in tandem with Formwerk like [vue-i18n](https://vue-i18n.intlify.dev/).
## Locale Config
Formwerk exposes a `locale` configuration option that you can set with the `configure` function. This is the main option Formwerk uses to determine locale and [directionality](#directionality).
```ts
import { configure } from '@formwerk/core';
configure({
locale: 'en-US',
});
```
The default locale is `en-US`.
### Locale auto-detection
By default Formwerk will auto-detect the locale using the html tag's `lang` attribute. It will not use the browser's locale since it may be different from the user's preferred language or the website's target audience.
There is no need to disable this feature since it detects the locale right when Formwerk is imported. So if you set the locale with the `configure` function, it will always respect that setting.
The auto-detection is not available in some environments like server-side rendering (SSR). In those cases you'll need to set the locale manually with the `configure` function.
## Directionality
Formwerk determines the directionality of the locale currently set. It does so using the [Intl.Locale](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale) API.
This determines the default `dir` property of Formwerk components. You can always override it with the `dir` prop on each composable or you can turn the auto-detection off with the `detectDirection` option.
```ts
import { configure } from '@formwerk/core';
configure({
detectDirection: false,
});
```
Not all composables react to the direction change, it is up to you to style them accordingly. But the functionality of some components may change as result, especially components that utilize the arrow keys (e.g: sliders).
## Usage with Vue i18n
Formwerk can be used with [vue-i18n](https://vue-i18n.intlify.dev/). Usually it is a good idea to set the locale in the app's main file or entry point where you initialize vue-i18n.
Formwerk accepts a reactive locale value and will automatically update the direction whenever the locale changes.
```ts
import { createI18n } from 'vue-i18n';
import { configure } from '@formwerk/core';
const i18n = createI18n({
locale: 'en-US',
});
configure({
// Reactive if legacy: false in vue-i18n options
locale: i18n.global.locale,
});
```
## Usage with Nuxt i18n
Since there is no `html` tag in SSR environments, Formwerk will not be able to auto-detect the locale. You'll need to set it manually with the `configure` function.
For Nuxt, you would be using [Nuxt i18n](https://i18n.nuxtjs.org/) module. It is recommended to set the locale in a [plugin](https://nuxt.com/docs/guide/directory-structure/plugins) to ensure it happens before Formwerk is used anywhere.
### Using the locale ref
Like with Vue i18n, Formwerk will automatically update the directionality whenever the locale changes if you pass a reactive locale value. So you only have one source of truth to manage.
```ts
import { configure } from '@formwerk/core';
export default defineNuxtPlugin((nuxtApp) => {
configure({
locale: nuxtApp.$i18n.locale,
});
});
```
### Using Nuxt Hooks
Alternatively, you can listen to the `i18n:localeSwitched` hook to update the locale and directionality whenever the locale changes with `setLocale` [returned from `useI18n`](https://i18n.nuxtjs.org/docs/guide/lang-switcher).
```ts
import { configure } from '@formwerk/core';
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('i18n:localeSwitched', ({ newLocale }) => {
configure({
locale: newLocale,
});
});
});
```
## HTML Validation Language Mismatch
HTML5 validation messages are generated in the browser's default language, not the website's. This means that if the user's browser is set to a different language than the website's target audience, the validation messages will be in the wrong language.
Unfortunately, there is no way to change the language of the validation messages after they are generated and there is no sufficient JS API to influence the message text. We are considering either building a tiny i18n layer just for this or rely on 3rd party to do it.
For now, the recommended workaround is to disable HTML5 validation messages generation and rely on [Standard Schemas](/guides/forms/validation#standard-schema-validation/) to provide the validation messages.
You still will be able to pass the `required` and other HTML validation attributes but they will not affect the validity or the messages once disabled.
---
--- title: Resources description: A list of resources for Formwerk, including articles, videos, podcasts, and more. ---
# Resources
If you have any resources that you think would be useful for the Formwerk community, please let us know and we'll add them to this page.
You can submit your resources by posting in our [Discord server](https://discord.gg/gQ7wqpvT5X).
---
--- title: Server Side Rendering description: How well does Formwerk work with server side rendering? ---
# Server Side Rendering
Server side rendering, or SSR, is the process of rendering components on the server and serve them as HTML initially, rather than rendering them on the client. Static Site Generation (SSG) is a similar approach, it renders pages to HTML files at build time rather than on each request. These techniques can help improve perceived loading performance and SEO.
Formwerk is designed to work with server side rendering. It does not rely on any client-side only APIs and should work in any environment that supports Vue.js SSR like Nuxt and Astro.
## Label and Input ID Generation
Formwerk builds on top of `useId` function exposed by Vue.js `>= v3.5.0` to generate unique IDs for labels and inputs. This is why Vue.js `v3.5.0` is the minimum required version for Formwerk.
## Nuxt Example
Formwerk works fully out of the box with Nuxt, import and use the composables in your Nuxt pages or components.
---
--- title: Styling description: Best practices for styling Formwerk components. ---
# Styling
import TextFieldPartial from './_partials/_textField.mdx';
import MdxRepl from '@components/MdxRepl.vue';
Formwerk doesn't ship with any CSS or HTML structure, it only provides the logic or the "soul" of the components. This means you can style it with your own CSS or use a CSS framework like Tailwind CSS.
Each composable field guide showed you a styled example, so we won't repeat them here. But we will cover some best practices for styling Formwerk components.
## Pseudo Classes
A crucial part of Formwerk's validation system is it uses the native HTML5 constraint validation API and ensures a consistent validity state even if you are using a Standard Schema. This means you can make use of pseudo classes like `:invalid` and `:valid` in CSS to style your fields regardless of the validation provider you use.
For many cases, this can allow you to control when to show or hide error messages without any JavaScript or conditional rendering.
### `:invalid` and `:valid`
The [`:invalid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:invalid) and [`:valid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:valid) pseudo classes are applied to the input element when the field is in an invalid or valid state.
Usually you **do not** want to use these pseudo classes for showing errors or applying error styles as they are too "aggressive".
Here is an example, notice how the style are already applied to the fields even before you have interacted with them.
```vue
```
### `:user-invalid` and `:user-valid`
The [`:user-invalid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:user-invalid) and [`:user-valid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:user-valid) pseudo classes are applied to the input element when the field is in an invalid or valid state after the user has interacted with the field. The criteria for "interaction" is determined by each browser but usually it is either of:
- User changed the value of the field.
- User submitted the form.
Here is an example making use of these pseudo classes, notice that the styles are only applied after you have interacted with the field.
```vue
```
### :disabled
The [`:disabled`](https://developer.mozilla.org/en-US/docs/Web/CSS/:disabled) pseudo class is applied to the input element when the field is disabled. This only works for components that use the HTML's `input` as a base element.
For non-input base elements you can use the `aria-disabled` attribute to target the field.
```vue
```
### `:focus`
The [`:focus`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus) pseudo class is applied to an element when it is focused. Formwerk manages the focus state for a lot of components, so this pseudo class is useful to style the focused element.
Other than highlighting the focused inputs, this can be critically important in a few other cases:
- Highlighting the focused `option` element in a `Select` component when navigating with the keyboard.
- Highlighting focused checkboxes and radio button items.
- Highlighting focused slider knobs.
## Aria Attributes
Formwerk components automatically generate the necessary `aria-*` attributes to ensure accessibility. You can take advantage of these attributes to style your fields based on their accessibility state.
These are the common aria attributes that Formwerk makes use of that you can style with:
| Attribute | Description |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `aria-required` | Indicates whether the element is required to fill out or not. Applied if the base element is NOT an `input`. |
| `aria-disabled` | Indicates whether the element is disabled or not. Applied if the base element is NOT an `input`. |
| `aria-invalid` | Indicates whether the element is invalid or not. Applied always. |
| `aria-selected` | Indicates whether the element is selected or not. Used for option components in a single selection `Select` fields. |
| `aria-checked` | Indicates whether the element is checked or not. For checkboxes and radios, applied if the base element is NOT an `input`. Also used for switches and option components in multiple selection `Select` fields. |
| `aria-orientation` | Indicates the orientation of the element. Commonly applied on sliders. |
| `aria-expanded` | Indicates whether the element is expanded or not. Commonly applied in `Select` fields. |
## `has()` selector
The `:has()` CSS pseudo class allows you to style an element based on whether it has a child element that matches a certain condition. This can eliminate a lot of unnecessary dynamic classes or JavaScript to conditionally style fields.
Here is an example that makes use of the `:has()` pseudo class to style the label of a field based on whether the field is required. The asterisk (`*`) is added to the label when the field is required.
```vue
```
Similarly you can do the same with any aria attributes or pseudo classes that would be applied to the field.
---
--- title: Composables description: Learn how to use the composables to build your own form controls. ---
# Composables
import PreviewCard from '@components/PreviewCard.vue';
## Fields
Each of these composables helps you build a high-quality and accessible form control component.
## Forms
These composables help you build and organize form components with a laser focus on developer experience and form management features.
---
--- title: Getting Started description: An introduction on how to get started with Formwerk ---
# Getting Started
## What is Formwerk?
Formwerk is a library of headless composables for Vue.js that helps you build accessible, high-quality form components for your applications or design systems.
It offers you a comprehensive tree-shakable set of composables that take care of accessibility, interaction, validation, and best behavior practices out of the box, allowing you to focus on your unique design and styling.
This library aims to offer an uncompromising new baseline of accessibility, interaction, and quality for building forms in Vue.
## Getting Started
1. Install Formwerk with your package manager of choice.
```bash
npm install @formwerk/core
```
```bash
pnpm add @formwerk/core
```
```bash
bun add @formwerk/core
```
```bash
yarn add @formwerk/core
```
2. Import the composable you need into your component.
```vue title="TextField.vue"
{{ errorMessage }}
```
3. Use your component!
```vue title="App.vue"
```
## Where to next?
You can either start using [the composables](/guides/composables/) to build your various form components, or you can start using the [starter kits](/starter-kits/minimal/) to get a head start on your next project.
Alternatively, follow the next guide to read our elevator pitch for Formwerk and why you might want to use it in your project. We'll also cover some of the core concepts and ideas that Formwerk is built on.
---
--- title: Why Formwerk? description: Why we think you should choose Formwerk over other form libraries. ---
# Why Formwerk?
### A Balance of Abstraction and Control
Forms are a crucial part of web applications and are one of the first things you learn as a web developer. Unfortunately, forms are hard.
There are too many expectations today on what forms should offer: data collection is non-negotiable, validation is nice to have, delightful interaction is desired, and accessibility is a must that often gets overlooked.
The web already offers a solid foundation on many of these aspects, but it falls short when it comes to using it in modern web applications due to the ever-increasing complexity in how we build our applications.
With many libraries to choose from, there isn't a one-size-fits-all solution. In the Vue ecosystem, we have a couple of schools of thought on how to build forms:
- **Headless Implementations:** By offering composables that help you handle some aspects of form building, like validation, and leave the rest up to you.
- **UI Libraries:** Whether or not they focus on forms, UI libraries often tick all of the feature boxes mentioned above and more. But they can force you into a "reward early, punish later" kind of situation. UI libraries are usually opinionated on their style and brand, and even if you can override or customize them, they are opinionated on the markup, which limits your ability to style them.
Both approaches try to balance two important aspects: Abstraction and Control. You lose some control when you abstract too much, and you lose function and introduce friction when you don't abstract enough.
We took a look at both approaches and found there yet exists a gap in the middle where a different balance of abstraction and control can be achieved.
We don't want the developer to write too much code with low-level abstraction, nor do we think they should fight a losing battle against styles and markup.
### So, Only JavaScript?
You might be wondering about being forced to fight the JavaScript and how it might be even harder than styles and markup.
Formwerk offers some opinionated choices, some of which are configurable. Maybe you don't want the mouse wheel to be used to increment a number field, or maybe you want to disable the native HTML validation for your fields or forms.
However, some decisions are not configurable. For example, you can't disable the keyboard interaction of a checkbox, nor the keyboard navigability of a select field's list box, as those will hurt your users.
Of course, nothing is set in stone, and we can always revisit decisions and make them configurable based on your feedback and use-cases.
### What Are You Getting
Formwerk offers a different approach to building forms. It provides a set of composables that help you build specific form components in a way that you are in full control over the markup and styling.
Each composable offers:
- First-class accessibility support with meticulous attention to common ARIA patterns.
- An adaptive API that adapts to the way you like to build forms. Hidden labels but still want the field accessible? We got you covered.
- Value tracking and collection, even with non-input elements.
- Behavior and user interaction via keyboard that adheres to each component's native HTML counterpart standard. Custom checkboxes behave exactly the same as native checkboxes.
- Multi-level form validation with native HTML constraints and custom validation schemas with popular libraries like Yup and Zod.
- First-class internationalization and RTL support.
- A consistent API for both simple and complex form components.
We don't intend to do everything, but rather we want to offer a solid foundation that helps you build high-quality forms that are both accessible and delightful to use.
That means there is no UI, no styles, no markup. That's up to you to build.
### Who Is Formwerk For?
Formwerk is for builders who want to build design systems from scratch and want to have a solid foundational baseline so that they can focus on the business logic and the brand design for many years to come. At the same time, if Formwerk doesn't work for them, they can easily dismantle it from their components.
Formwerk is also for UI library developers who want to offer a solid offering of form components to their users. No point for those authors to re-implement the same behavior that users expect today in form components. In other words, if you as an author use Formwerk, you can focus on your library's main offering: delightful UI and a great developer experience.
Formwerk is not for:
- Those who use existing UI libraries.
- Those who are looking to prototype fast. Building with Formwerk is fast, but not as fast as using a UI library.
## Why Not ....?
All those libraries are excellent at what they do, and you won't go wrong with any of them, assuming you understand the trade-offs.
### vee-validate
I'm the author of vee-validate, and I'm proud of what we've built there for 7+ years now. It does many things well.
However, I consider it to be a bit low-level for building form components. There is still a lot of stuff you have to implement yourself. vee-validate doesn't cover accessibility, internationalization, interaction, nor behaviors, which is why many users pair it with other UI libraries.
Formwerk is an attempt to offer a more complete solution for building forms by stepping up the abstraction level a notch or two. Formwerk takes a lot of key learnings from vee-validate and applies them broadly to form building.
### FormKit
> FormKit is more than a UI library. It is comprehensive with a lot of features and great developer experience.
FormKit is a great library that tackles everything about forms really well, including UI. But this is where FormKit and Formwerk differ; we think UI is personal and should be tailored to your brand. No matter how much you can configure or override by slots or by props, you are one design away from considering a rewrite. Granted, this does not happen often.
This "reward early" approach will definitely help you build forms 10x faster. But if you have to dismantle it, you'll be punished. Formwerk, on the other hand, is as easy to dismantle as it is to build with.
Formwerk doesn't offer any UI. You own your markup, you own your styles. For everything else, Formwerk has you covered. You can build your UI with either plain CSS, Tailwind, or that cool new thing you found on BlueSky last week. There is nothing to override, nothing to configure.
### Vuelidate
A great option for validation. But out of all the options available, it is one with the lowest level of abstraction.
This means you have control over every other aspect. Albeit by omission rather than design, this means you have to implement everything except the validation.
### Tanstack Forms
Formwerk and Tanstack forms agree on offering a headless approach to form building. However Tankstack lives on the lower end of the abstraction scale.
This means while it offers the tools needed to build fields, it doesn't cater to each field's unique needs in terms of accessibility, interaction, and behavior.
Formwerk addresses form concerns like internationalization on a broader scale. It goes beyond just localized messages, ensuring that international users can interact with forms naturally. This includes support for different numbering systems, various date formats and calendars, and more.
On the other hand, Formwerk offers less flexibility in certain aspects like when to validate a field, but that's mostly due to philosophical differences, you can read more about it in the [Validation](/docs/guides/validation) guide.
---
--- title: Minimal Starter Kit description: A minimal starter kit for Formwerk, a great starting point for your next project with custom styles. ---
# Minimal Starter Kit
We plan to have a CLI and details document for this starter kit soon, for now head over to the [GitHub repo](https://github.com/formwerkjs/formwerk/tree/main/packages/starter-kits/minimal/src/components) to get started.
---
--- title: Form Groups description: Learn how to group fields and slice forms into smaller parts ---
# Form Groups
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import Kbd from '@components/KeyboardKey.vue';
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyFormGroup from '@components/AnatomyFormGroup.vue';
import TextFieldPartial from '../fields/_partials/_textField.mdx';
import CheckboxPartial from '../fields/_partials/_checkbox.mdx';
import FieldsetPartial from './_partials/_fieldset.mdx';
import GroupPartial from './_partials/_group.mdx';
Form groups are a way to structure related fields in a form. They allow you to group fields together and slice forms into smaller parts. This can be useful for organizing forms with many fields or for creating reusable group components that may be added to multiple forms.
Their main purpose is to slice forms into manageable parts. They are not nested forms; they do not submit data on their own. They are just a way to group fields together in a parent form.
## Features
- Multi-layered validation with both HTML attributes or [Standard Schemas](https://github.com/standard-schema/standard-schema).
- Aggregated state for validation, dirty, touched, and more.
- Supports either `fieldset` and `legend` elements or any other HTML elements.
- Automatic field name prefixing for organizing submitted data structure.
## Anatomy
## Creating a Form Group
To create a form group, you can use the `useFormGroup` composable. Just like fields, the `useFormGroup` composable returns values that should be bound to the anatomy elements.
Typically, you need to create a `FormGroup` component that you can use to structure your form fields. You can use `fieldset` as a base element for your `FormGroup` component or any other element that you prefer.
Form groups require a `name` prop, which will be used to nest the fields' values in the form data. In the next examples, fill out the data and submit the form to see how the form data are structured.
### With a fieldset element
```vue
```
Naturally, styling is entirely up to you. This can be much easier with other HTML elements like `div`, as shown in the next section.
### With any HTML element
You can use any HTML element to create a form group. Formwerk's `useFormGroup` bindings automatically change based on the element that is bound to it to ensure users get a consistent experience regardless of what element you choose to use.
Here's an example using a `div` element:
```vue
```
## Validation
The form guide mentioned briefly that groups can have their own validation [Standard Schema](https://github.com/standard-schema/standard-schema) just like forms do. They do not override the parent form schema; they are just an extension of it.
Formwerk prioritizes the validation sources in the following order:
1. **HTML Constraints** are checked first; only if they are valid, continue to the next step.
2. **Field-level's** Standard Schema is checked next; only if it is valid, continue to the next step.
3. **Group-level's** Standard Schema is checked next; only if it is valid, continue to the next step.
4. **Form-level's** Standard Schema is checked last.
```vue
```
## Group state
The group tracks and aggregates the fields' state that are part of it. The following states are an aggregation of the fields that are part of the group:
| Name | Type | Description |
| ----------- | ------- | --------------------------------------------------------------------------- |
| `isDirty` | Boolean | Indicates whether any field within the group has been modified. |
| `isValid` | Boolean | Indicates whether all fields within the group pass their validation checks. |
| `isTouched` | Boolean | Indicates whether any field within the group has been interacted with. |
It also exposes the following getters and functions:
| Name | Type | Description |
| -------------- | ------------------------------------- | ------------------------------------------------------------------------------- |
| `getErrors` | () => IssueCollection[] | Returns all errors within the group. |
| `getValues` | () => Record\ | Returns all values within the group. |
| `getError` | (name: string) => string \| undefined | Returns the error for a given field within the group. |
| `displayError` | (name: string) => string \| undefined | Displays the error for a given field within the group if the field was touched. |
| `validate` | () => Promise | Validates all fields within the group. |
## Group Names and Nested Paths
A group can accept a `name` prop, which will prefix all the field names nested under it with that same name.
This means that when submitting the form, the data will be nested under the group name.
```vue
```
## API
### Props
These are the properties that can be passed to the `useFormGroup` composable.
### Returns
These are the properties in the object returned by the `useFormGroup` composable.
---
--- title: Form Repeaters description: Learn how to create a form repeater ---
# Form Repeaters
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyRepeater from '@components/AnatomyRepeater.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import RepeaterPartial from './_partials/_repeater.mdx';
import TextFieldPartial from '../fields/_partials/_textField.mdx';
import CheckboxPartial from '../fields/_partials/_checkbox.mdx';
Form repeaters, also known as field arrays, are a way to create a list of fields that may repeat multiple times in a form. This is useful for creating forms that collect a dynamic number of items, like a list of emails or addresses. It can also be used for a group of fields that fill an object's information.
## Features
- Adding, removing, and swapping items.
- Auto-name prefixing for nested fields.
- Min and Max item count enforcement.
- Accessibility for add/remove and move buttons is automatically managed.
## Anatomy
## Creating a Form Repeater
To create a form repeater, you can use the `useFormRepeater` composable.
Typically, you need to create a `FormRepeater` component that you can use to structure your form fields. There is no specific element that you need to use at this time, as this is considered a utility container.
At its simplest, the `useFormRepeater` composable returns the items you need to render the repeater.
```ts
import { useFormRepeater, FormRepeaterProps } from '@formwerk/core';
const props = defineProps();
const { items, Iteration } = useFormRepeater(props);
```
Note that the `Iteration` component used here is a component that is returned by the `useFormRepeater` composable. This component is a wrapper that is used to render each item in the repeater in a managed way.
Under the hood, the `Iteration` component prefixes the nested fields with the repeater's name and the index of the item.
The `Iteration` default slot props contain the following properties:
- `removeButtonProps`: The props for the remove button.
- `moveUpButtonProps`: The props for the move up button.
- `moveDownButtonProps`: The props for the move down button.
The `items` array is a readonly array of strings that represent the keys of the items in the repeater. These are unique identifiers for each item in the repeater, and you should not attempt to set them manually.
Putting it all together, you get a simple example like this:
```vue
#{{ index + 1 }}
```
## Example
Here is a fully styled example that makes use of some of the fields we created in the field guides. We also integrate a `TransitionGroup` from Vue to animate the entering and leaving of the repeater items. This is why we are not abstracting the looping aspect of the repeater, as it gives you the flexibility to use any animation library/tool you like.
```vue
```
## Usage
### Min and Max Items
You can enforce a minimum and maximum number of items in a repeater by passing the `min` and `max` props. The remove and add buttons will be disabled when the minimum or maximum number of items is reached.
The repeater will also automatically add items to match the minimum number of items when the form is first rendered.
```vue
```
### Field Path Prefixing
Repeaters require a `name` prop to know which part of the form they are going to repeat. Any fields nested within a repeater will have a name that is prefixed with the repeater's name and the index of the item. This is done automatically for you.
You can observe this by submitting the previous examples.
#### Unnamed Fields
As you've learned previously in the [form guide](/guides/forms/), not having a name prop on the field will make it an uncontrolled field. This is also true for repeaters; however, if you pass an empty string as the field name, the field would still be controlled and will set its value to the iteration path directly.
Try adding new items to the following example and fill out the email fields. Notice how the values are set on the array indices directly.
```vue
{{ values }}
```
If you need a field to be uncontrolled, avoid passing a name prop to the field.
## API
### Props
These are the properties that can be passed to the `useFormRepeater` composable.
### Returns
These are the properties in the object returned by the `useFormRepeater` composable.
### ``
#### Props
#### Slot Props
These are the properties passed to the default slot of the `Iteration` component.
---
--- title: Checkboxes description: Learn how to build accessible Vue.js checkbox components with Formwerk. ---
# Checkboxes
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyCheckboxes from '@components/AnatomyCheckboxes.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import Kbd from '@components/KeyboardKey.vue';
import CheckboxPartial from './_partials/_checkbox.mdx';
import CheckboxCustomPartial from './_partials/_checkboxCustom.mdx';
import CheckboxGroupPartial from './_partials/_checkboxGroup.mdx';
import CheckboxGroupPartialMixed from './_partials/_checkboxGroupMixed.mdx';
Checkboxes allow the user to toggle the field state on (checked) or off (unchecked). They are mainly used in a couple of scenarios:
- **A single checkbox:** Used for binary choices, such as acknowledging terms and conditions in sign-up forms. Typically, this is represented by a boolean value.
- **A checkbox group:** Used to represent multiple choices, such as updating email marketing preferences. This can be represented by an array of values for each selected choice.
Checkboxes are more nuanced than that, and we've put a lot of thought into how they should behave as components.
[Aria Patterns](https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/)
## Features
You can build checkboxes using either native HTML `input[type="checkbox"]` elements or other HTML elements. We provide behavior, state, and accessibility implementation for both cases with the same API and features.
Currently, the following features are implemented:
- Support for either `input[type="checkbox"]` or any other HTML elements as a base element for the checkbox.
- Automatic linking of labels, descriptions, and error messages to input and label elements with `aria-*` attributes.
- Custom checked/unchecked values.
- Support for the `indeterminate` state.
- Form management, data collection, and validation with [Standard Schemas](https://github.com/standard-schema/standard-schema) or native HTML5 validation.
- Support for orientation with `horizontal` and `vertical` values.
- `v-model` support for both single and grouped checkboxes.
- Supported Keyboard features:
| Key | Description |
| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| | Selects the focused checkbox item. |
| | Moves the focus to the next checkbox item or the next tab order element in the document. |
|
+
| Moves the focus to the previous checkbox item or the previous tab order element in the document. |
## Anatomy
## Building a Checkbox component
The `useCheckbox` composable provides the behavior, state, and accessibility implementation for checkbox items. Checkbox items can be built with either `input[type="checkbox"]` or custom HTML elements depending on your styling needs.
You can start by importing the `useCheckbox` composable and using it in your checkbox component.
```ts
import { CheckboxProps, useCheckbox } from '@formwerk/core';
const props = defineProps();
const { labelProps, inputProps } = useCheckbox(props);
```
The `useCheckbox` composable returns binding objects for the elements shown in the anatomy. You will use `v-bind` to bind them to the DOM elements.
```vue
```
These bindings contain all the necessary attributes and event listeners to manage the checkbox state and behavior and to ensure support for assistive technologies.
With that out of the way, let's see how you can build a checkbox component. There are two ways to do it.
### With `input` as a base element
This is how you would build a custom checkbox item component using the `useCheckbox` composable.
With the native `input` being limited in terms of styling, you can consider hiding it visually and instead use a custom element in its place. Note that while the element is visually hidden, it will still be announced by assistive technologies. Alternatively, you can use any element as a base instead.
The `.sr-only` class is used to hide the `input` element from the visual layout but keep it accessible to assistive technologies. You should have a similar class in your styling solution or framework as this use-case is very common.
Another thing to note is we've placed the `input` element inside a `label` element to ensure that the checkbox is still focusable and that clicking on the label also toggles the checkbox.
```vue
checked: {{ isChecked }}
```
### With other HTML elements as a base
For special styling needs, you don't have to use the `input` element. You can use a custom HTML element as a base with the same API.
```vue
checked: {{ isChecked }}
```
## Building a Checkbox Group component
You will use the `useCheckboxGroup` composable to build a checkbox group component. The composable offers the behavior, state, and accessibility implementation for checkbox groups.
Assuming that you've already built a checkbox component using the `useCheckbox` composable as the two are tightly coupled, the following example shows how you can build a checkbox group component.
We will use a checkbox without an `input` element base just to show you that it does not matter what element you use as a base. The same behaviors will be provided for either.
```vue
selected colors: {{ colors }}
```
## Validation
Checkbox components and checkbox groups can be validated by using [Standard Schemas](https://github.com/standard-schema/standard-schema) or native HTML5 validation via the `schema` prop on each of them.
When checkboxes are a part of a checkbox group, they will not report their own error messages as the group component should be responsible for displaying the error message.
### HTML Constraints
The following properties are supported on both composables if you use the `input` element as a base for the checkbox.
| Name | Type | Description |
| ---------- | --------- | ------------------------------------------ |
| `required` | `boolean` | Whether the checkbox or group is required. |
Here is an example of how to use the `required` property on either a `CheckboxItem` or a `CheckboxGroup`.
```vue
```
Note that marking any checkbox item that is part of a checkbox group as required will not make the group required and it will be ignored.
### Standard Schema
Both `useCheckbox` and `useCheckboxGroup` support [Standard Schema](https://github.com/standard-schema/standard-schema) validation through the `schema` prop. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
```vue
```
## Usage
### Disabled
You can disable individual checkboxes or the whole group with the `disabled` prop on either. Disabled checkboxes are not focusable. Disabled groups are not submitted and are not validated.
```vue
```
If you need to prevent the user from interacting with the checkboxes while still allowing it to submit, consider using `readonly` instead.
### Readonly
Only available on the group and group-less checkboxes. The `readonly` prop prevents the user from interacting with the group/checkbox while still allowing it to submit and be validated.
```vue
```
### Indeterminate
Only available on the checkbox item. The `indeterminate` state is used to model a tri-state checkbox. One of the situations it is used in is when a checkbox group has a mix of checked and unchecked items.
Checkboxes that are indeterminate will change their checked state until the indeterminate state is removed.
```vue
```
### Group Checked State
The overall checked state of a group is reported via the `groupState` property on the `useCheckboxGroup` composable. The `groupState` property is an enum that represents if the group is entirely `checked`, `unchecked`, or `mixed`.
We will use a few SVGs to represent the group state visually.
```vue
```
## Checkbox Value Type and Generics
By default, the value of a checkbox is a boolean. You can change the value type by passing in the generic type to the `CheckboxProps` or `CheckboxGroupProps` types.
The following example shows how to change the value type to a string.
```ts
import { useCheckbox, type CheckboxProps } from '@formwerk/core';
const props = defineProps>();
// ...
```
But ideally, to make sure your component is type-safe and as flexible as possible, you should use generics on the component itself.
```vue
```
You can do the same for the `CheckboxGroup` components as well.
```vue
```
## API
### Checkbox
#### Props
These are the properties that can be passed to the `useCheckbox` composable.
#### Returns
These are the properties in the object returned by the `useCheckbox` composable.
### Checkbox Group
These are the properties that can be passed to the `useCheckboxGroup` composable.
#### Props
These are the properties in the object returned by the `useCheckboxGroup` composable.
#### Returns
These are the properties in the object returned by the `useCheckboxGroup` composable.
---
--- title: Validation description: Learn how validation works in Formwerk ---
# Validation
import MdxRepl from '@components/MdxRepl.vue';
import TextField from '../fields/_partials/_textField.mdx';
Form validation is one of the most important features of any form library. The main goal of a client-side validation library isn't protection or security, but to provide a better user experience.
By providing more immediate feedback on the client side, the user can fix their mistakes before submitting the form. This has a couple of benefits:
- Reduces the likelihood of a form being submitted with invalid data, allowing your server to process requests that are more likely to succeed.
- Provides a better user experience by giving immediate feedback on the client side.
Formwerk bakes validation into any of the component composables that you use. This means that you don't need to worry about implementing validation logic yourself.
Formwerk makes use of a couple of different validation systems:
- **HTML Constraint Validation API:** Done via the various validation attributes that you can add to your form fields like `required`, `minlength`, `maxlength`, `min`, `max`, `type`, etc.
- **Standard Schema Validation:** A schema-based validation that uses JavaScript implemented by multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), and [Arktype](https://arktype.io/).
Both systems can be used together, and you can even mix between them.
## HTML Constraint Validation API
The HTML constraint API at its core is pretty simple. It's a set of attributes that you can add to your form fields, and they enforce rules on the field's value.
Each field has some attributes that can be set; some of these attributes are specific to the type of the field. Each field guide shows you which attributes are available for that field, so we won't cover them in detail here.
However, you need to keep in mind some caveats around this:
- Rules like `maxLength` for text fields or `min/max` for number fields are "preventative". This means they don't allow values that violate the rule or they prevent violation. Unlike other attributes like `minLength` and `required`, which are "suggestive", in other words, the user can violate them and see an error message. In either case, both prevent submission.
- Some validation rules are applied implicitly based on the field's type. For text fields, setting a `[type="email"]` will automatically apply the browser's email validation. Similarly, other types like `[type="url"]` will apply URL validation. These rules are also "suggestive", as in the user can violate them and see an error message.
- Validation messages are always set in the user's language. If the user's locale is German, for example, the message will be in German regardless of the language of your application.
Given these caveats and the limited capabilities for advanced cases, many apps today prefer to use a schema-based validation library like Zod or Yup, which is also supported by Formwerk.
### Disabling HTML5 Validation
In some cases, you may need to disable HTML validation messages. One common reason is [language mismatch between the browser and the website](/extras/i18n/#html-validation-language-mismatch).
You can disable HTML5 validation by setting `disableHtmlValidation` to `true`. This option exists as a prop on fields, forms, form groups, or as a global configuration with the `configure` function.
```ts
import { configure } from '@formwerk/core';
configure({
disableHtmlValidation: true,
});
```
## Standard Schema Validation
Formwerk supports the [Standard Schema Spec](https://github.com/standard-schema/standard-schema) and leverages it to provide a uniform schema validation with full type safety for all libraries that support it.
At the moment, the following providers implement the Standard Schema Spec and are supported by Formwerk:
- [Zod](https://zod.dev/)
- [Valibot](https://valibot.dev/)
- [Arktype](https://arktype.io/)
Here is an example that uses Valibot:
```ts
import * as v from 'valibot';
import { useForm } from '@formwerk/core';
const schema = v.object({
email: v.pipe(v.string(), v.email()),
password: v.pipe(v.string(), v.minLength(8)),
});
const { values, handleSubmit } = useForm({
schema,
});
```
### Form Types
As mentioned in the form guide, standard schemas automatically offer type inference for the form's current values and the submit values. This is incredibly useful to avoid having to cast and re-check values when submitting them just because TypeScript isn't aware of the runtime validation.
When using a Standard Schema, you don't have to do anything special to get these benefits. Types are automatically inferred from the given schema.
```ts
import { useForm } from '@formwerk/core';
import * as v from 'valibot';
const schema = v.object({
email: v.pipe(v.string(), v.email()),
password: v.pipe(v.string(), v.minLength(8)),
});
const { values, handleSubmit } = useForm({
schema,
});
values; // { email?: string | undefined; password?: string | undefined }
const onSubmit = handleSubmit((data) => {
data.toObject(); // { email: string; password: string }
});
```
The same logic applies to form groups; however, form groups do not have output types.
```ts
import { useFormGroup } from '@formwerk/core';
import * as v from 'valibot';
const schema = v.object({
email: v.pipe(v.string(), v.email()),
password: v.pipe(v.string(), v.minLength(8)),
});
const { getValues } = useFormGroup({
schema,
});
getValues(); // { email?: string | undefined; password?: string | undefined }
```
## Unified Validation API
As you have probably noticed in the field guides, Formwerk unifies standard schema validation with the HTML constraint validation API.
Under the hood, if using a Standard Schema, Formwerk takes the errors and sets them on the field's `validationMessage` property. That means you can make use of pseudo-classes like `:invalid` and `:valid` in CSS to style your fields regardless of the validation system you use. We made use of this a few times in the styled examples.
## Validation Triggers and Error Display
Formwerk always displays the error messages regardless of whether the user has interacted with the field or not. This is done to avoid confusion for you as the developer and puts you in control.
So instead of "When to validate?", you can think of it as "When to display errors?" which is less complicated and less prone to errors.
### Validation Events
Formwerk by default validates on the following events:
- `blur`: When a field loses focus.
- `change`: When a field changes its value.
- `submit`: When a form is submitted.
- `click`: For some types of fields like `checkbox` or `radio`.
- `invalid`: An event that fires if the field becomes invalid as a result of HTML constraint validation.
At this moment, you can't change the default validation events.
### Error Display
Now that you know you will always have errors available, you can decide when to display them.
All Formwerk components expose a few properties that can help you with error display:
- `errors`: An array of all error messages if the field is invalid.
- `errorMessage`: The error message if the field is invalid. Always the first element of the `errors` array.
- `isTouched`: A boolean indicating if the field has been interacted with (blurred).
- `isValid`: A boolean indicating if the field is valid.
- `isDirty`: A boolean indicating if the field's value has changed.
- `displayError`: A function that returns the error message if the field has been touched and is invalid.
Given these properties, you can mix between them to produce the desired behavior.
Here's an example that shows errors only when the field is touched:
```vue
{{ errorMessage }}
```
```vue
```
Notice that even though the error message is present initially, it is still not displayed. This is because the field is not yet touched.
You can shorten this logic by using the `displayError` function.
```vue
{{ displayError() }}
```
```vue
```
---
--- title: Calendars description: Learn how to build accessible calendar Vue components with Formwerk. ---
# Calendars
import Kbd from '@components/KeyboardKey.vue';
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyCalendar from '@components/AnatomyCalendar.vue';
import CalendarPartial from './_partials/_calendarPartial.mdx';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
Calendars are used to allow users to select a date, for example to select a date of birth or a date for a reservation.
Calendar components in Formwerk can be used with a [date field](/guides/fields/date-fields/) or as a standalone component.
## Features
- Labeling, Descriptions, and error messages are automatically linked to the calendar elements.
- Supports different views (week, month, year) to allow for large date range navigation.
- Support for multiple calendars (e.g: `islamic-umalqura`, `japanese`, etc).
- Supports minimum and maximum date boundaries.
- Validation support with native HTML constraints or [Standard Schemas](https://github.com/standard-schema/standard-schema).
- Auto directionality based on the locale.
- Cell states for today, selected, disabled and outside month dates.
- Support for `v-model` binding.
- Comprehensive keyboard shortcuts support.
### Keyboard Features
#### Week view
| Key | Description |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| | Moves the focus to the same day of the next week. |
| | Moves the focus to the same day of the previous week. |
| | Moves the focus to the previous day. |
| | Moves the focus to the next day. |
| | Moves the focus to the first day of the current month. If already on the first day, moves the focus to the first day of the previous month. |
| | Moves the focus to the last day of the current month. If already on the last day, moves the focus to the last day of the next month. |
| | Moves the focus to the same day of the previous month. |
| | Moves the focus to the same day of the next month. |
#### Month view
| Key | Description |
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| | Moves the focus to the next quarter. |
| | Moves the focus to the previous quarter. |
| | Moves the focus to the previous month. |
| | Moves the focus to the next month. |
| | Moves the focus to the first month of the year. If already on the first month, moves the focus to the first month of the previous year. |
| | Moves the focus to the last month of the year. If already on the last month, moves the focus to the last month of the next year. |
| | Moves the focus to the same month of the previous year. |
| | Moves the focus to the same month of the next year. |
#### Year view
| Key | Description |
| -------------------------------------------- | ---------------------------------------------------- |
| | Moves the focus to the next year row (+3 years). |
| | Moves the focus to the previous year row (-3 years). |
| | Moves the focus to the previous year. |
| | Moves the focus to the next year. |
## Anatomy
## Building a Calendar Component
You can start by importing the `useCalendar` composable and using it in your calendar component.
The `useCalendar` composable returns binding objects for the elements shown in the [anatomy](#anatomy). You will use `v-bind` to bind them to the corresponding DOM elements.
```vue
```
Note that Formwerk already exposes a `CalendarCell` component that renders a span by default for convenience. It provides handling for user interactions and accessibility attributes.
You can build your own calendar cell component and customize it as needed with `useCalendarCell` composable.
```vue
{{ label }}
```
## Validation
### HTML Constraints
You can use the following properties to validate the calendar field with HTML constraint validation:
| Name | Type | Description |
| ---------- | --------- | ------------------------------------- |
| `min` | `Date` | The minimum date that can be entered. |
| `max` | `Date` | The maximum date that can be entered. |
| `required` | `boolean` | Whether the date field is required. |
### Standard Schema
## Usage
### Disabled
Use `disabled` to mark calendar as non-interactive. Disabled calendars are not validated and are not submitted.
If you need to prevent the user from interacting with the calendar while still allowing it to submit, consider using `readonly` instead.
```vue
```
### Readonly
Readonly calendars are validated and submitted, but they do not accept user input. The calendar is still focusable. For more info, check the [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly).
```vue
```
### Locale and Calendar Support
The `calendar` prop can be used to specify which calendar to use, along with the `locale` prop to set the locale of the calendar.
You can use `@internationalized/date` to create calendar objects. In this example, we will use the `islamic-umalqura` calendar along with the `ar-SA` locale.
```vue
```
### Min and Max
You can pass `min` and `max` props to set the minimum and maximum dates that can be selected. You will still need to handle styling those dates, out of range dates will be marked with `aria-disabled` attribute.
```vue
```
### Calendar as a picker
You can use the `Calendar` component as a picker which can be useful to pair with a [date field](/guides/fields/date-fields/).
To do that, you can use the `usePicker` composable:
```vue
Selected date: {{ date || 'none' }}
```
In that example, we are using the popover API, but you can use any other floating UI solution you prefer.
### Disabling calendar views
The calendar will have the 3 views enabled by default. You switch between the views by clicking the header of the calendar.
While we do not recommend disabling the views as your users may expect being able to navigate with them, you can still allow specific views by passing the `allowedViews` prop.
In the following example, we are disabling the year view:
```vue
```
Now if you click the header of the calendar, you will get the months view, if you click again you will remain on the months view rather than going to the years view.
## API
### useCalendar
#### Props
These are the properties that can be passed to the `useCalendar` composable.
#### Returns
These are the properties in the object returned by the `useCalendar` composable.
### useCalendarCell
#### Props
These are the properties that can be passed to the `useCalendarCell` composable.
#### Returns
These are the properties in the object returned by the `useCalendarCell` composable.
---
--- title: ComboBox Fields description: Learn how to build accessible ComboBox field Vue components with Formwerk. ---
# ComboBox Fields
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyComboBox from '@components/AnatomyComboBox.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import Kbd from '@components/KeyboardKey.vue';
import OptionPartial from './_partials/_option.mdx';
import OptionGroupPartial from './_partials/_optionGroup.mdx';
import ComboPartial from './_partials/_combobox.mdx';
ComboBoxes are common form fields. They combine text fields with a dropdown or a listbox to allow users to select an option from the list that matches the text they have entered.
## Features
- Labeling, descriptions, error message displays are automatically linked to input and label elements with `aria-*` attributes.
- Support for single selections.
- Support for option groups.
- Support for option filtering with multiple filtering strategies.
- First-class Support for `[popover]` popups for dropdowns/menus.
- Generic typing support for options.
- Validation support with HTML validation attributes and [Standard Schemas](https://github.com/standard-schema/standard-schema).
- Support for `v-model` binding.
- Supported Keyboard features:
When focusing the text input, and menu is closed:
| Key | Description |
| ------------------------------------------ | ----------------------- |
| | Opens the options menu. |
| | Opens the options menu. |
| | Clears the input value. |
When focusing the text input, and menu is open:
| Key | Description |
| ------------------------------------------- | -------------------------------------------- |
| | Selects the highlighted option. |
| | Highlights the next option if available. |
| | Highlights the previous option if available. |
| | Highlights the first option. |
| | Highlights the first option. |
| | Highlights the last option. |
| | Highlights the last option. |
| | Closes the options menu if open. |
Most people aren't aware of these keyboard shortcuts, but they do make a big difference in the user experience for keyboard users.
## Anatomy
## Building a ComboBox Field
The combobox field is one of the most complex fields with multiple moving parts. It is what Formwerk considers a "compound component" because it is made up of an ecosystem of components that work together to create a single field.
From the anatomy diagram above, you can see that the core combobox field is made up of the following parts:
- **Input:** A text input that is used to filter the options and trigger the Listbox.
- **Button:** A trigger element that is used to open the Listbox popup.
- **Listbox:** An options menu that contains the list of options.
- **Option:** An option component that represents each option in the list.
- **Option Group:** Not illustrated above, an option group that groups options together based on some categorization.
Formwerk handles all of these parts and abstracts them into the following component ecosystem:
- **ComboBox**: Contains the input, button, and listbox parts. You will use `useComboBox` to build it.
- **Option**: Represents an option in the list. You will use `useOption` to build it.
- **OptionGroup**: Represents a group of options in the list. You will use `useOptionGroup` to build it.
Notice that the listbox is not a separate component. This is because popups today can be done in different ways, so Formwerk while offers the open state along with some accessability attributes, it does not have a specific popup component implementation. But we offer out of the box support for [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API) if you happen to use one for the listbox element.
### Building an Option Component
We can start by using `useOption` to create our option component. This component will be responsible for rendering each option in the list.
Notice that we are using the `OptionProps` type to define the props for our component. This type is generic and allows you to specify the type of the option value. In this case, we are using `any` to represent any type of value.
The `label` is available inside `OptionProps` and we will display it to the user. Option components can also be used in other fields like [select fields](/guides/fields/selects/).
### Building a ComboBox Component
Next, you will use `useComboBox` to create our combobox component. This component will be responsible for rendering the trigger and popup parts.
The `useComboBox` composable returns binding objects for some of the elements shown in the [anatomy](#anatomy), you will use `v-bind` to bind them to the corresponding DOM elements.
```vue
```
### Building an Option Group Component
Building an option group component is similar to building an option component. You will use `useOptionGroup` to create the component.
```vue
```
OptionGroup components can also be used in other fields like [select fields](/guides/fields/selects/).
## Validation
Because selects in Formwerk are a fully custom component, it doesn't support any HTML validation attributes. You can however, use Standard Schemas to validate the value of the select.
In the future when customizable selects are available in the browser, Formwerk will leverage that to provide better validation support for the native HTML constraints.
### Standard Schema
`useComboBox` supports [Standard Schema](https://github.com/standard-schema/standard-schema) validation through the `schema` prop. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
```vue
```
## Filtering
### Default Filter
You probably have noticed we are using `useDefaultFilter` to filter the options using the `contains` strategy. By default, comboboxes do not know how to filter the options.
Aside from the `contains` strategy, Formwerk also offers the `startsWith`, `endsWith` and `equals` strategies.
```ts
import { useDefaultFilter } from '@formwerk/core';
const { startsWith, endsWith, equals } = useDefaultFilter({
caseSensitive: false,
});
```
### Debouncing
Filtering runs on every keystroke, but you can debounce it by passing the `debounceMs` option to `useDefaultFilter`.
```ts {5}
import { useDefaultFilter } from '@formwerk/core';
const { startsWith, endsWith, equals } = useDefaultFilter({
caseSensitive: false,
debounceMs: 300,
});
```
## Usage
### Disabled
Use disabled to mark fields as non-interactive, disabled fields are not validated and are not submitted.
If you need to prevent the user from interacting with the field while still allowing it to submit, consider using `readonly` instead.
```vue
```
You can also mark individual options as disabled by passing a `disabled` prop to the `OptionItem` component. Note that disabled options are skipped from the focus order when using shortcuts or the search functionality.
```vue
```
### Readonly
Readonly fields are validated and submitted, but they do not accept user input. The field is still focusable and the popup is still openable. For more info, check the [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly).
```vue
```
### Open listbox on focus
You can open the listbox on focus by passing the `openOnFocus` prop.
```vue
```
### Custom Values
ComboBoxes by default do not allow custom values, you probably noticed that every time you blurred the text field without making a selection, the input value was reverted to the last value.
If you need to allow custom values, you can pass the `onNewValue` prop/handler to `useComboBox`. The `onNewValue` prop is a function that must return the both the new value and the label to use for it.
```vue
```
The `onNewValue` handler is called when all of the following conditions are met:
- The current input value does not match any option value.
- The current input value is not empty.
- The user presses the `Enter` or the `Tab` keys, blurring the text field otherwise has no effect.
You can also return `null` or `undefined` to prevent the new value from being added to the list.
```vue {13-16}
```
## Styling
When styling the select field, you need to be aware of the following...
#### Option attributes
When an option is focused, it will receive the `aria-selected="true"` attribute.
#### Input attributes
The text input element will receive the `aria-expanded` attribute. So you can style the trigger according to the popover state if needed.
It will also receive the `aria-activedescendant` attribute when an option is focused.
## API
### Option
#### Props
These are the properties that can be passed to the `useOption` composable.
#### Returns
These are the properties in the object returned by the `useOption` composable.
### Option Group
#### Props
These are the properties that can be passed to the `useOptionGroup` composable.
#### Returns
These are the properties in the object returned by the `useOptionGroup` composable.
### ComboBox
#### Props
These are the properties that can be passed to the `useComboBox` composable.
#### Returns
These are the properties in the object returned by the `useSelect` composable.
### useDefaultFilter
---
--- title: Forms description: Learn how to build forms in your application with Formwerk. ---
# Forms
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import Kbd from '@components/KeyboardKey.vue';
import TextFieldPartial from '../fields/_partials/_textField.mdx';
import CheckboxPartial from '../fields/_partials/_checkbox.mdx';
Forms allow users to fill and submit data to your application, sometimes they are given feedback along the way to help them achieve that.
Assuming you've followed the other guides and have created a few input components, you can now use them to build a form. Formwerk builds on the native `form` element but it is not required.
## Features
- Value tracking and submission handling.
- Nested fields and arrays support.
- Controlled and uncontrolled fields.
- Multi-layered validation with both HTML attributes or [Standard Schemas](https://github.com/standard-schema/standard-schema).
- Aggregated state for validation, dirty, touched, and more.
- Type safety for form data and submitted data.
- Scrolling to the first invalid field after submission.
- Submitted data can be consumed as a plain object, JSON, or `FormData` object.
## useForm
You will be using the `useForm` composable to create a form context in the current component. This effectively marks the component as a form, meaning you can only use `useForm` once per component.
This is the most basic form you can create with Formwerk:
```ts
import { useForm } from '@formwerk/core';
const { handleSubmit } = useForm();
const onSubmit = handleSubmit((data) => {
console.log(data);
});
```
It doesn't look like much, but already a lot is being done for you behind the scenes. The `useForm` composable creates a reactive form context that does the following among other things:
- Tracks and collects the values of all input fields within the form.
- Tracks the validity of each field and the overall form validity.
- Provides a `handleSubmit` function that you can use to submit the form.
Here is an example with some input fields we already created from the previous guides:
```vue
```
## Controlled Fields
You may have noticed that we passed the `name` prop to the input fields in the previous example. This is because Formwerk uses the `name` prop to identify the fields in the form and uses it to build the form data object that will eventually be submitted.
Passing the `name` prop to the field marks it as "controlled", as in it is being tracked by the form and contributes its state and value to the form data object.
Now if you want to do the opposite, which is to not have the field be tracked by the form, then you can simply skip passing the `name` prop. This matches the behavior of `FormData` objects and native form submission behavior.
Here is an example where a non-controlled field can be useful. In this example, we toggle the visibility of the `billingAddress` field based on the value of the `sameAsShipping` field, but we don't want to submit the latter.
```vue
```
## Nested Fields
Formwerk supports nested fields by using the `.` character in the `name` prop. This allows you to create nested objects in the form data object to structure your data however you need. Having numeric path names will result in arrays being created instead of objects.
Here is an example with both nested fields and arrays:
```vue
```
## Submitting Forms
You noticed that the values of the form are collected and passed for you in the `data` object in the previous examples.
### handleSubmit
The previous examples used the `handleSubmit` function to submit the form. This function doesn't require you to use a `form` element nor does it require you to use it with a `submit` event. You can use it with any event or even call it directly.
The `handleSubmit` function takes a callback function that will be called with the `data` object when the form is submitted. The callback is run only if the form is valid; otherwise, it does nothing.
Here is an example where we just call the submission handler directly:
```vue
```
#### toObject
The most common way to get the form data is to call the `toObject` method on the `data` object. This method returns a plain JavaScript object with the form data as you've seen in the previous examples.
```ts
import { useForm } from '@formwerk/core';
const { handleSubmit } = useForm();
const onSubmit = handleSubmit((data) => {
data.toObject(); // { email: '...', password: '...', rememberMe: true }
});
```
#### toJSON
The `toJSON` method returns a JSON-serializable object with the form data. While this often matches the structure from `toObject`, JSON has some limitations compared to JavaScript objects. For example, JSON cannot represent:
- `undefined` values
- Date objects (they get converted to strings)
- File objects
- Functions
- BigInt values
- Symbol values
The `toJSON` method handles converting these values into JSON-safe equivalents automatically.
```ts
import { useForm } from '@formwerk/core';
const { handleSubmit } = useForm();
const onSubmit = handleSubmit(async (data) => {
const response = await fetch('https://example.org/post', {
body: JSON.stringify(data.toJSON()),
});
});
```
If you plan to stringify the form data for use with something like `fetch` or `axios`, then you can omit the `toJSON` and call `JSON.stringify` directly on the `data` object, which will call `toJSON` under the hood.
```ts del={3} ins={4}
const onSubmit = handleSubmit(async (data) => {
const response = await fetch('https://example.org/post', {
body: JSON.stringify(data.toJSON()),
body: JSON.stringify(data),
});
});
```
The `toJSON` method is fully typed to match the JSON-serialized structure of your form data. The types will automatically handle non-serializable values like `undefined`, `Date` objects, and `File` objects in the type system, ensuring the types match what actually gets serialized to JSON.
#### toFormData
If you need to submit the form data as a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object, you can call the `toFormData` method instead.
This method returns a `FormData` object that you can use to submit the form data to traditional form endpoints or APIs. It becomes especially useful when submitting files since they cannot be transported in JSON.
```ts
import { useForm } from '@formwerk/core';
const { handleSubmit } = useForm();
const onSubmit = handleSubmit((data) => {
data.toFormData(); // FormData
});
```
### formProps
If you prefer to not handle submissions with JavaScript and instead want to rely on native form submissions, which is common with non-JS server-rendered applications like Rails (Ruby) or Laravel (PHP) applications, you can use the `formProps` object that is returned by the `useForm` composable to bind the form props to the `form` element. It will enhance the native submission cycle with the same features as with the `handleSubmit` function.
When you submit a form bound to the `formProps` object:
1. The form submit event will be prevented.
2. The form data will be collected and validated.
3. If invalid, the form will not be submitted, and the flow ends.
4. If valid, the form will be submitted using the native form submission cycle.
Here is an example of how to use `formProps`. The example will submit the data to another page that will list the submitted values. Typically, your backend endpoint would be handling the form submission.
```vue
```
### Submit State
Forms and Fields expose several properties related to the submission state. They can be useful to build certain UI behaviors like showing submit progress spinners or disabling the submit button while the form is being submitted with an async handler.
#### isSubmitting
You can check the submission status with the `isSubmitting` property. This property is `true` when the form is being submitted and `false` otherwise. This is useful when you want to show a loading spinner or disable the submit button while the form is being submitted with an async handler.
```vue
```
#### wasSubmitted
The `wasSubmitted` property is `true` if the form was submitted and the handler was called without any errors thrown. This is useful when you want to show a success message or perform some custom logic after the form has been submitted.
```vue
Form was submitted
```
This state is reset when the form is [reset](#resetting-forms).
#### submitAttemptsCount
The `submitAttemptsCount` property returns the number of times the form has been submitted regardless of whether it was valid or not.
This can be useful when you want to disable certain UI elements, or show some feedback to the user. You might even want to gather analytics! maybe your form is too hard?
```vue
Submit attempts: {{ submitAttemptsCount }}
```
This state is reset when the form is [reset](#resetting-forms).
#### isSubmitAttempted
The `isSubmitAttempted` property is `true` if the form was submitted, but unlike `wasSubmitted`, it turns `true` even if the form is invalid.
```ts
import { useForm } from '@formwerk/core';
const { handleSubmit, isSubmitAttempted } = useForm();
const onSubmit = handleSubmit((data) => {
console.log(JSON.stringify(data.toObject(), null, 2));
});
```
### Scrolling to invalid fields
By default, Formwerk will scroll to the first invalid field _when the form is submitted_.
The scrolling is performed with the [`Element.scrollIntoView`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#sect1) method with smooth behavior by default. You can override that by passing [`ScrollViewOptions`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#sect1) to the `scrollToInvalidFieldOnSubmit` option.
```ts
import { useForm } from '@formwerk/core';
useForm({
scrollToInvalidFieldOnSubmit: {
behavior: 'instant', // default is 'smooth'
block: 'center', // default is 'center'
inline: 'start', // default is 'start'
},
});
```
You can see it in action in the following example. Scroll all the way down to see the form submit button and then click it to see the invalid field being scrolled into view.
```vue
```
This behavior can be disabled by setting the `scrollToInvalidFieldOnSubmit` option to `false` when creating the form.
```ts
import { useForm } from '@formwerk/core';
useForm({
scrollToInvalidFieldOnSubmit: false,
});
```
## Touched Fields
Forms track the touched state of each field in the form. A field is considered touched when the user interacts with it, which means if they have focused and blurred the field at least once. In addition to blurring, whenever the form is submitted, all fields are marked as touched.
The form also tracks the overall touched state of the form, which is `true` if any field in the form has been touched.
Each field composable returns an `isTouched` property. `useForm` exposes its own `isTouched` method that you can use to check if the form has been interacted with or if a field has been touched.
```vue
```
You can set the touched state of fields manually with the `setTouched` function.
```ts
import { useForm } from '@formwerk/core';
const { setTouched } = useForm();
function onFieldBlur() {
setTouched('email', true);
// Or set all fields to touched
setTouched(true);
}
```
## Dirty Fields
Forms also track the dirty state of each field in the form. A field is considered dirty when its value has changed from the initial value. The form also tracks the overall dirty state of the form, which is `true` if any field in the form has been modified.
```vue
Email Dirty: {{ isDirty('email') }}
Password Dirty: {{ isDirty('password') }}
Form Dirty: {{ isDirty() }}
```
The dirty state is computed. There is no way to set it manually, but you can reset the form to its initial values or a new set of values to influence the dirty state.
## Validation
As you've seen from field guides, many fields can be validated with either HTML constraints via attributes like `required`, `min`, `max`, etc., or with [Standard Schema](https://github.com/standard-schema/standard-schema) objects.
### HTML Constraints
HTML constraints are always field-level. They are useful for dynamic fields, but at the same time, they are more accessible to users, which is why it is recommended to use them whenever possible for basic validations.
For advanced cases, you can use Standard Schemas, which can be both field-level or form-level.
If you want to completely disable HTML constraints for the form, you can pass the `disableHtmlValidation` option to `useForm`:
```ts
import { useForm } from '@formwerk/core';
useForm({
disableHtmlValidation: true,
});
```
### Form-level Validation with Standard Schemas
But you can also provide a form-level [Standard Schema](https://github.com/standard-schema/standard-schema) to `useForm` to validate the entire form as a whole. Form-level schemas are useful for forms where the fields are known beforehand.
Here is an example of a form with a Standard Schema:
```vue
```
For more information on Standard Schemas and which libraries are supported, visit the project's [GitHub page](https://github.com/standard-schema/standard-schema).
### Mixing validation sources
Let's say you have a mix of validations in place. You have a field with some HTML constraints and a schema that validates that field at the same time. Let's throw in a form-level schema that validates the form, including that field.
That field now has three sources of validation. How does that work?
Formwerk prioritizes the validation sources in the following order:
1. HTML Constraints are checked first. Only if they are valid, continue to the next step.
2. Field-level Standard Schema is checked next. Only if it is valid, continue to the next step.
3. Form-level Standard Schema is checked last.
This keeps the validation process consistent and predictable. At the same time, it is also efficient, as you won't have to re-validate the whole form if a field-level validation fails for that field. You can think of it as a merged validation approach, but it is more of a cascading validation behavior where it cascades upwards to the form level.
The only thing you need to be careful of is to not have conflicting validations between the different sources, as this can cause the field to never be valid.
Here is an example for a field with all validation sources:
```vue
```
You can even have a fourth source of validation with [Form Groups](/guides/forms/form-groups/).
## Displaying Errors
If you have followed the field guides, you know that fields are responsible for displaying their own errors. But what if you want to display the form errors in a single place, or maybe you just need access to errors to perform some custom logic?
There are three ways to access errors with `useForm`:
- `getError` to get the error of a specific field.
- `getErrors` to get all errors in the form grouped by field.
- `displayError` to display the error of a specific field if it has been touched.
The `getError` function returns the error of a specific field. If the field has no error, it returns `undefined`.
```vue
URL Error: {{ getError('url') }}
Email Error: {{ getError('email') }}
```
The `getErrors` function returns all errors in the form as an array of error groups. Each group contains a field error message.
```vue
{{ error.path }}: {{ error.messages }}
```
The `displayError` function is similar to `getError`, but it only displays the error if the field has been touched. This is useful when you want to show errors only after the user has interacted with the field.
You can alternatively use CSS with the `:user-invalid` pseudo-class to show errors only when the field is invalid and has been interacted with. More info on that in the [Styling guide](/extras/styling/).
```vue
URL Error: {{ displayError('url') }}
Email Error: {{ displayError('email') }}
```
## Submit Errors
Unlike validation errors which are mostly "live" and react to the values changing regardless of when you display them, submit errors are only populated when the form is submitted. This is useful if you want to only show errors after submits rather than live.
Each field exposes `submitErrorMessage` and `submitErrors`, using these will only display errors after the form is submitted.
```ts
import { useTextField } from '@formwerk/core';
const { submitErrorMessage, submitErrors } = useTextField({
// ...
});
```
Forms also expose `getSubmitError` and `getSubmitErrors` to get the submit error of a specific field or all fields respectively, if you need access to them on the form level.
## Resetting Forms
### with `reset()`
Form state can be reset with the `reset` function. Calling this function will reset the current values back to the initial values, revert the touched state for all fields back to `false`, and clear any custom errors.
```vue
```
Notice that even though we called reset, the errors are still displayed. By default, reset re-validates the form afterward. This is to ensure that the validation state of the fields matches their actual validity.
You should consider using `isTouched` or `displayError` to show errors only when the field has been interacted with, which would eliminate this caveat. Alternatively, you can disable this behavior by passing `revalidate: false` to the `reset` function.
```ts
import { useForm } from '@formwerk/core';
const { reset } = useForm();
function onReset() {
reset({ revalidate: false });
}
```
You can also reset the form to a specific state by passing a `ResetState` object to the `reset` function. This object can contain the following properties:
- values: The new form values.
- touched: The new touched state for each field.
```vue
```
Lastly, the value setting behavior by default uses a `replace` strategy. This means that the values are replaced with the new values, and any unspecified values will be considered `undefined`.
If you want to merge the new values with the existing initial values, you can pass `behavior: 'merge'` to the `reset` function as the second argument.
```ts
import { useForm } from '@formwerk/core';
const { reset } = useForm();
function onReset() {
reset(
{
// values and stuff...
},
{ behavior: 'merge' },
);
}
```
### event handler with `handleReset()`
You can also use `handleReset()` to create an event handler that responds to the native `reset` event.
```vue
```
## Form Types
Forms support typing your form values with TypeScript. This is done through a couple of generic type parameters.
The `useForm` composable signature is roughly typed as:
```ts
function useForm<
TSchema extends StandardSchema,
TInput extends FormObject = FormObject,
TOutput extends FormObject = TInput,
>();
```
Let's break down the generic type parameters:
- `TSchema`: The type of the form schema if a [Standard Schema](https://github.com/standard-schema/standard-schema) is used.
- `TInput`: The type of the form input values. These represent the current values of the form fields without any validation or transformations applied. We also refer to those as "input" types.
- `TOutput`: The type of the form output values. These represent the values that would be submitted. That means validation and transformations have already been applied.
The distinction between input and output types is important because it helps you avoid re-checking values that have already been validated in runtime to satisfy TypeScript.
### Inferring Types with `initialValues`
You can type a form by either providing `initialValues` to infer the input type.
```ts
const { values, handleSubmit } = useForm({
initialValues: { email: '' },
});
values; // { email?: string | undefined }
```
However, this does not provide you with output types, meaning when submitting the form, the `email` field would still be typed as `string | undefined`.
In order to get output types, we export a utility type called `FormSchema` that you can use to type both the input and output values. By default, the input type is assumed to be a partial of the output type.
```ts
import { type FormSchema, useForm } from '@formwerk/core';
// Input type is assumed to be a partial of the output type
type LoginForm = FormSchema<{ email: string }>;
const { handleSubmit, values } = useForm();
values; // { email: string | undefined }
const onSubmit = handleSubmit((data) => {
console.log(data.toObject()); // { email: string }
});
```
If you want to explicitly define both the input and output types, you can do so by passing a second generic argument to the `FormSchema` type. The first being the input type and the second being the output type.
```ts
import { type FormSchema, useForm } from '@formwerk/core';
type LoginForm = FormSchema<
{ email: string },
{ email: string; token: string }
>;
const { handleSubmit, values } = useForm();
values; // { email: string | undefined; }
const onSubmit = handleSubmit((data) => {
console.log(data.toObject()); // { email: string; token: string }
});
```
We only recommend using this approach for simple forms with a few fields, or if the types are automatically generated from an API schema like GraphQL or OpenAPI specs.
### Inferring Types with Standard Schema
By providing a [Standard Schema](https://github.com/standard-schema/standard-schema) to the `schema` prop, the form will infer both the input and output types automatically.
```ts
import { z } from 'zod';
const { values, handleSubmit } = useForm({
schema: z.object({ email: z.string().email() }),
});
values; // { email: string | undefined }
const onSubmit = handleSubmit((data) => {
data.toObject(); // { email: string }
});
```
For getting the most out of type safety, it is recommended to use Standard Schemas over manually providing types via the `initialValues` prop.
## Getting access to the form context
You may need to access the form context from within a component that is a child of the form. Common examples are button components that may need to be aware of the submitting state or the dirty state.
To do this, you can use the `useFormContext` composable.
```ts
import { useFormContext } from '@formwerk/core';
const { isSubmitting } = useFormContext();
```
## API
### Props
These are the properties that can be passed to the `useForm` composable.
### Returns
These are the properties in the object returned by the `useForm` composable.
---
--- title: Custom Fields description: Learn how to build custom fields with Formwerk. ---
# Custom Fields
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyCustomField from '@components/AnatomyCustomField.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import SpeechCustomField from './_partials/_speechCustomField.mdx';
Custom fields are fields that do not fit into the existing composable categories. In other words, they are a possibly complex widget that participates into your forms.
Such fields have no specific UI as they are your own creation. Those fields might be used in very specific business domains, you will see a few examples of these in the page.
## Features
- Labels, descriptions, and error message displays are automatically linked to control and label elements with `aria-*` attributes.
- Validation support with [Standard Schema](https://github.com/standard-schema/standard-schema) validation.
- Support for `v-model` binding.
## Anatomy
## Building a Custom Field Component
Now comes the fun part. Use your imagination to build a new kind of a field. This can be hard to do on demand, so we will instead build a "speak a sentence" field. Similar to what you would find in language learning apps like Duolingo.
We will start by importing the `useCustomField` composable and using it in our custom field component.
The `useCustomField` composable returns binding objects for the elements shown in the [anatomy](#anatomy). You will use `v-bind` to bind them to the corresponding DOM elements. The `control` element is anything that qualifies as the main interactive part of the input. In other words, it is the visual part that the user interacts with to enter the value.
Now the value type of such a field could be a `string` as in you detect what the user says on the microphone and you present it as a string. We can use the [`SpeechRecognition` API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API/Using_the_Web_Speech_API) to detect what the user says on the microphone.
Alternatively, we can just build the full experience of the field and make it so that it auto checks what the user says and if it matches the sentence, it will be marked as correct. This means the value type of the field will be a boolean rather than a string. It is up to you and your requirements, but here we will go with a boolean type.
```vue
```
## Validation
`useCustomField` supports [Standard Schema](https://github.com/standard-schema/standard-schema) validation through the `schema` prop. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
```vue
```
### Triggering Validation
Custom fields lack any signal to trigger validation. This is because we don't know what kind of elements you will be using, so you will have to trigger validation manually via the `validate` function exposed by the `useCustomField` composable.
```vue
```
## Wrapping 3rd Party Components
Another use-case for custom fields is to wrap 3rd party components. Maybe you are using a UI library and would like to integrate it with Formwerk, custom fields are perfect for this since they have no expectations about the kind of markup you will use.
However, this means you will have to hook up the moving parts yourself. In the following example, we are wrapping a 3rd party text input component from PrimeVue.
The validation here is aggressive, it is up to you to decide when to show the error message.
Keep in mind that this is an inefficient use of Formwerk, here you only gain validation state and the form API, since most UI libraries already have their accessibility and interactions built in. But, if you need to, this is how you could do it.
## Usage
### Disabled
For custom fields, it is up to you to implement the disabled state. We don't know what disabled means for your field, so we leave it up to you to implement it.
The disabled state is returned from `useCustomField` as `isDisabled` and it is one of the props accepted by the `useCustomField` composable. It behaves the same as other fields' disabled state, if a parent form or form group is disabled, the field will be disabled.
What Formwerk does however, is it will not validate the field when it is disabled and it will not submit the field when it is disabled either.
### Readonly
For custom fields, it is up to you to implement the readonly state. We don't know what readonly means for your field, so we leave it up to you to implement it.
Readonly fields are validated and submitted, but they do not accept user input. The field is still focusable, and the value is copyable in the case of text fields. So use this as your north star, make sure the field is focusable, but not editable.
## API
### Props
These are the properties that can be passed to the `useCustomField` composable.
### Returns
These are the properties in the object returned by the `useCustomField` composable.
---
--- title: File Fields description: Learn how to build accessible file field Vue components with Formwerk. ---
# File Fields
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyFileField from '@components/AnatomyFileField.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import DropzonePartial from './_partials/_dropzone.mdx';
import FileFieldPartial from './_partials/_fileField.mdx';
File fields are a type of input field that allows users to submit or upload files.
## Features
- Uses `input` element.
- Labeling, Descriptions, and error messages are automatically linked to their corresponding elements.
- Validation support with native HTML constraint validation or [Standard Schema](https://github.com/standard-schema/standard-schema) validation.
- Support for `v-model` binding.
- Basic upload handling support.
- Multiple file selection support.
- Drag and drop support for "dropzone" components.
- Auto previews for images and videos.
## Anatomy
## Building a File Field Component
You can start by importing the `useFileField` composable and using it in your file field component.
The `useFileField` composable returns binding objects for the elements shown in the [anatomy](#anatomy). You will use `v-bind` to bind them to the corresponding DOM elements.
```vue
```
## Building a Dropzone Component
You can use `useFileField` to build a "dropzone" component that allows users to drag and drop files to upload them.
In this case, we can use the `FileEntry` component to render the file entry, the `FileEntry` component exposes various slot props to manage, and preview the file entry if it is an image or a video.
```vue
```
## Validation
### HTML Constraints
You can use the following properties to validate the file field with native HTML constraint validation:
| Name | Type | Description |
| ---------- | --------- | ----------------------------------- |
| `required` | `boolean` | Whether the file field is required. |
Here is an example of how to use the `required` property to make the file field required.
```vue
```
### Standard Schema
You can use the `useFileField` composable to validate the file field with [Standard Schema](https://github.com/standard-schema/standard-schema).
In this example, we are validating the file size to be 1MB maximum.
```vue
```
### Mixed Validation
All file fields created with Formwerk support mixed validation, which means you can use both HTML constraints and Standard Schema validation to validate the field, and they work seamlessly together.
Note that HTML constraints are validated first, so any errors from the HTML constraints will be displayed first. Then, once all HTML constraints are satisfied, the Standard Schema is validated.
Here we are mixing both the `required` HTML constraint and the Zod size validation.
```vue
```
## Usage
### Disabled
Use `disabled` to mark fields as non-interactive. Disabled fields are not validated and are not submitted.
```vue
```
One important thing is to no forget the `dropzoneProps` binding object, which contains the listeners needed to make the dropzone work.
You can also build your own `FileEntry` component with the `useFileEntry` composable.
This dropzone component isn't multiple by default, but you can use the `multiple` prop to allow users to select multiple files.
### Multiple Files
You can use the `multiple` prop to allow users to select multiple files.
```vue
```
### Allowing Picking Directories
You can use the `allowDirectory` prop to allow users to pick directories if the file is `multiple`.
```vue
```
### Uploading Files
The `useFileField` composable accepts an `onUpload` handler that is called with the file to be uploaded. The `onUpload` handler receives a `FileUploadContext` object with the following properties:
The upload handler should return a string value that will be swapped with the file in the form when it submits. The string value is usually an identifier that your server can use to identify the file.
```vue
Field value: {{ value }}
```
### Initial Data
At the moment, `useFileField` doesn't support initial data because you cannot really set a "file" value to an `input` element due to security restrictions. There are a few ideas we have like:
- Given a URL, we reconstruct the file object and set it as the initial value.
- Create a "fake" file object that we can set as the initial value.
We are open to suggestions, join our [Discord server](https://discord.gg/sT73ZGUy7X) or [open an issue](https://github.com/formwerk/formwerk/issues) if you have any ideas!
## API
### useFileField
#### Props
#### Returns
### useFileEntry
#### Props
#### Returns
---
--- title: Date Fields description: Learn how to build accessible date field Vue components with Formwerk. ---
# Date Fields
import Kbd from '@components/KeyboardKey.vue';
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyDateField from '@components/AnatomyDateField.vue';
import MdxRepl from '@components/MdxRepl.vue';
import DateFieldPartial from './_partials/_dateFieldPartial.mdx';
import CalendarPartial from './_partials/_calendarPartial.mdx';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import DateFieldPartialWithCalendar from './_partials/_dateFieldPartialWithCalendar.mdx';
import PreviewCard from '@components/PreviewCard.vue';
Date fields are a type of input field that allows users to enter a date. They are a common feature in many web applications, and Formwerk provides a `useDateTimeField` composable that can be used to create date fields in your application.
## Features
- Labeling, Descriptions, and error messages are automatically linked to the date field elements.
- Support for multiple calendars (e.g: `islamic-umalqura`, `japanese`, etc).
- Supports minimum and maximum date boundaries.
- Validation support with native HTML constraints or [Standard Schemas](https://github.com/standard-schema/standard-schema).
- Auto directionality based on the locale.
- Focus management and auto navigation for date segments.
- Support for `v-model` binding.
- Comprehensive keyboard shortcuts support.
### Keyboard Features
| Key | Description |
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| | Decrements selected segment by 1. |
| | Increments selected segment by 1. |
| | Moves the focus to the previous segment. |
| | Moves the focus to the next segment. |
| | Clears the current segment. |
| | Clears the current segment. |
| | Moves the focus to the next segment or next element in the tab index order if it is the last segment. |
## Anatomy
## Building a Date Field Component
Just like the [native HTML date field](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date), the date field in Formwork is compromised of date segments, each segment represents a part of the date/time value (e.g: day, month, year, hour, minute, etc).
The segments are generated for you automatically based on the `formatOptions` you pass to the `useDateTimeField` composable. You will need to bind the prop objects returned by the composable to the corresponding DOM elements in the [anatomy](#anatomy).
If you do not pass the `formatOptions` prop, the date field will use the default format options for the locale of your app.
```vue
```
The `DateTimeSegment` component is used to render each segment of the date field. We provide one for convenience that you can use. You can also build your own with `useDateTimeSegment` composable.
```vue
{{ label }}
```
## Validation
### HTML Constraints
You can use the following properties to validate the date field with native HTML constraint validation:
| Name | Type | Description |
| ---------- | --------- | ------------------------------------- |
| `min` | `Date` | The minimum date that can be entered. |
| `max` | `Date` | The maximum date that can be entered. |
| `required` | `boolean` | Whether the date field is required. |
```vue
```
### Standard Schema
`useDateTimeField` supports [Standard Schema](https://github.com/standard-schema/standard-schema) validation through the `schema` prop. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
In this example, we are validating that the date field is between January 1st, 2025 and December 31st, 2025.
```vue
```
### Mixed Validation
All date fields created with Formwerk support mixed validation, which means you can use both HTML constraints and Standard Schema validation to validate the field, and they work seamlessly together.
Note that HTML constraints are validated first, so any errors from the HTML constraints will be displayed first. Then, once all HTML constraints are satisfied, the Standard Schema is validated.
```vue
```
## Usage
### Disabled
Use the `disabled` prop to disable the date field. Disabled date fields are not validated and are not submitted.
If you need to prevent the user from interacting with the field while still allowing it to submit, consider using `readonly` instead.
```vue
```
### Readonly
Use the `readonly` prop to make the date field read-only. Read-only date fields are still validated and submitted.
```vue
```
### Min and Max
You can pass `min` and `max` props to set the minimum and maximum dates that can be entered, not only they are used for validation but they affect the date segments that are available for selection and interaction.
```vue
```
Notice in the previous examples, some parts of the date field are disabled.
This is because when providing both a `min` and a `max`, Formwerk checks the possibilities of the date segments, and if a segment has only one possible value then it automatically sets it and disables it. Just like the native `input[type="date"]` element.
### Format Options
You can pass any `Intl.DateTimeFormatOptions` to the `formatOptions` prop to customize the date field's display format.
```vue
```
### Locale and Calendar Support
The `calendar` prop can be used to specify which calendar to use, along with the `locale` prop to set the locale of the date field.
You can use `@internationalized/date` to create calendar objects. In this example, we will use the `islamic-umalqura` calendar along with the `ar-SA` locale.
```vue
```
## Usage with Calendar Component
You can use the `DateField` component with the `Calendar` component to allow users to select a date. First check out the [Calendar](/guides/fields/calendars/) guide to build your own calendar component so that you can use it in the next example.
```vue
```
## Examples
These are some examples of date fields built with Formwerk.
## API
### useDateField
#### Props
These are the properties that can be passed to the `useDateField` composable.
#### Returns
These are the properties in the object returned by the `useDateField` composable.
### useDateSegment
#### Props
These are the properties that can be passed to the `useDateSegment` composable.
#### Returns
These are the properties in the object returned by the `useDateTimeSegment` composable.
---
--- title: Hidden Fields description: Learn how to use hidden fields in Formwerk. ---
# Hidden Fields
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyHiddenField from '@components/AnatomyHiddenField.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
Hidden fields let you include data that cannot be seen or modified by users when a form is submitted. A good example is a CSRF token or an ID for the content being submitted.
As such, they do not have most of the features of other fields.
## Features
- Offers a `useHiddenField` composable and a renderless `HiddenField` component.
## Anatomy
## Hidden Field Component
Unlike other fields, you can use the `HiddenField` component to create a hidden field without having to create your own components. This is because we view the hidden field as a utility field for declaring values declaratively.
```vue
```
Alternatively, you can use the `useHiddenField` composable to create a custom hidden field component.
```vue
```
To demonstrate how hidden fields work, we will use a `form` in the following example. We will try to keep it as simple as possible, but you can check the [form guide](/guides/forms/) for more information.
```vue
```
## Validation
Hidden fields do not support validation, although their [API](#api) supports showing any possible error messages that the form might have for the field. The main reason for this is that hidden fields are, well, hidden. That means the user cannot interact with them, and as such, you should ensure they always have valid values; otherwise, it will be confusing to the user.
## Usage
### Disabled
Marking a hidden field as disabled will make it non-submittable, meaning its value will not be included in the form data when the form is submitted.
```vue
```
## API
### Props
These are the properties that can be passed to the `useHiddenField` composable and the `HiddenField` component.
### Returns
These are the properties in the object returned by the `useHiddenField` composable.
---
--- title: Number Fields description: Learn how to build accessible and i18n-ready number field Vue components with Formwerk. ---
# Number Fields
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyNumberField from '@components/AnatomyNumberField.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import Kbd from '@components/KeyboardKey.vue';
import MouseWheel from '@components/MouseWheel.vue';
import PreviewCard from '@components/PreviewCard.vue';
import NumberFieldPartial from './_partials/_numberField.mdx';
Number fields are a common field in many forms. They include built-in validation to reject non-numerical input. Additionally, the browser may provide stepper arrows to let the user increase and decrease the value with a configurable step amount.
The Number field is usually used for number values rather than numeric values. For example while a credit card number is numeric, you should not use a number field for it. Instead you should use it for values that are meant to be consumed as a number like units, prices and percentages.
The native HTML number input and most other implementations do not offer a good experience. Here are a couple of common issues:
- **Lack of proper internationalization:** Mainly, the lack of support for other numeral systems like the Arabic numerals `(٠١٢٣٤٥٦٧٨٩)` are a pain to work with. Users often have to switch keyboard languages/layout to enter numbers and even then it is not perfect. Keyboards don't always have all the necessary characters for decimals and thousands separators. This means the number input is not accessible for the global audience.
- **No formatting support:** this includes grouping `,` and displaying units and currencies and other simple masking features.
Formwerk tries to address all these issues and more by utilizing the `Intl.NumberFormat` API. It provides a solid foundation for building number fields that are accessible and easy to use for users all over the world.
## Features
- Supports using the `input` element as a base with `type="text"` (don't worry, we add the `inputmode` automatically for accessability and mobile keyboards).
- Labeling, descriptions, error message displays are automatically linked to input and label elements with `aria-*` attributes.
- Formatting and parsing numbers with the `Intl.NumberFormat` API depending on the site locale or the user's preferred language.
- Support for multiple numeral systems (Latin, Arabic, and Chinese).
- Support for `Intl.NumberFormat` units and currencies.
- Support for the [Spinbutton ARIA pattern](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/) for increment/decrement buttons.
- Support for `min`, `max` and `step` attributes.
- Validation support with native HTML constraint validation or [Standard Schema](https://github.com/standard-schema/standard-schema) validation.
- Rejects non-numerical input characters and any incoming key presses that would make the number invalid.
- Support for `v-model` binding.
- Supported Keyboard features:
| Key | Description |
| ---------------------------------------------- | -------------------------------------------------------------------- |
| | increment the value by the step amount. |
| | decrement the value by the step amount. |
| | increment the value by larger multiple of the step amount. |
| | decrement the value by larger multiple of the step amount. |
| | set the value to the min value if provided, otherwise has no effect. |
| | set the value to the max value if provided, otherwise has no effect. |
| | increment the value by the step amount. |
| | decrement the value by the step amount. |
## Anatomy
## Building a Number Field Component
You can start by importing the `useNumberField` composable and using it in your number field component.
The `useNumberField` composable returns binding objects for the elements shown in the [anatomy](#anatomy), you will use `v-bind` to bind them to the corresponding DOM elements.
```vue
```
Notice that we imported the `NumberFieldProps` in the previous example. This is recommended to use as your component prop types. Not only you get type safety for your component out of it but also it handles the reactivity aspects of the props so you don't have to. You are free to extend it with your own props or omit the ones you don't need.
## Validation
### HTML Constraints
You can use the following native HTML validation properties to validate the number field:
| Name | Type | Description |
| ---------- | --------- | --------------------------------------- |
| `max` | `number` | The maximum value for the number field. |
| `min` | `number` | The minimum value for the number field. |
| `required` | `boolean` | Whether the number field is required. |
| `step` | `number` | The step amount for the number field. |
Here is an example of how to use the `max` and `min` properties to limit the number field value between 0 and 100.
Assuming you have a `NumberField` component like the one shown above, you can use it like this:
```vue
```
### Standard Schema
`useNumberField` also supports [Standard Schema](https://github.com/standard-schema/standard-schema) validation through the `schema` prop. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
```vue
```
### Mixed Validation
While it is unlikely that you need both HTML constraints and Standard Schemas to validate a number field, Formwerk supports mixed validation, which means you can use both HTML constraints and Standard Schemas to validate the field and define step amount and min/max values and they work seamlessly together.
Note that HTML constraints are validated first, so any errors from the HTML constraints will be displayed first. Then once all HTML constraints are satisfied, the Standard Schema is validated.
```vue
```
## Usage
### Disabled
Use disabled to mark fields as non-interactive, disabled fields are not validated and are not submitted.
If you need to prevent the user from interacting with the field while still allowing it to submit, consider using `readonly` instead.
```vue
```
### Readonly
Readonly fields are validated and submitted, but they do not accept user input. The field is still focusable and the value is copyable. For more info, check the [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly).
```vue
```
### Formatting and Units
You can use the `formatOptions` prop to format the number field value. It accepts all [options](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options) that are supported by the `Intl.NumberFormat` API.
```vue
```
### i18n
Aside from [formatting](#formatting-and-units) you can also use any numeral system supported by the `Intl.NumberFormat` API. Like Arabic, and Chinese.
The number field also accepts a `locale` prop to change the locale of the number field. Usually you should not want to pass it manually but for demonstration purposes it is shown below.
Actually, here are 3 fields each with a different numeral system bound to the same value, and you get the parsed value with either of them.
```vue
value is: {{ value }}
```
### RTL
The number field doesn't really need much for RTL support, however the `dir` prop can be used to set the direction of the field for convenience.
```vue
```
## Examples
These are some examples of number fields built with Formwerk.
## API
### Props
These are the properties that can be passed to the `useNumberField` composable.
### Returns
These are the properties in the object returned by the `useNumberField` composable.
---
--- title: OTP Fields description: Learn how to build accessible OTP field Vue components with Formwerk. ---
# OTP Fields
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyOtp from '@components/AnatomyOtp.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import Kbd from '@components/KeyboardKey.vue';
import OtpFieldPartial from './_partials/_otpField.mdx';
import MdxRepl from '@components/MdxRepl.vue';
OTP fields are used to allow users to input a one-time password or a code. Usually used for 2FA (MFA) or authentication purposes.
## Features
- Use `input` or any other element to create an OTP field and its slots.
- Labels, descriptions, and error message displays are automatically linked to input and label elements with `aria-*` attributes.
- Validation support with native HTML constraint validation or [Standard Schema](https://github.com/standard-schema/standard-schema) validation.
- Support for `v-model` binding.
- Supports masking (hiding) the entered characters with a custom character.
- Supports prefixes (e.g. `F-{code}`).
- Supports custom length.
- Supports both numeric and alphanumeric OTPs.
- Comprehensively supports keyboard navigation.
- Auto management of focus during user interaction.
- Handles paste events.
- Auto submit on completion.
### Keyboard Features
| Key | Description |
| -------------------------------------------- | --------------------------------------------------------------------- |
| | Moves the focus to the next OTP slot. |
| | Moves the focus to the previous OTP slot. |
| | Clears the current OTP slot and moves the focus to the previous slot. |
| | Moves the focus to the next OTP slot. |
| | Moves the focus to the next OTP slot. |
## Anatomy
## Building an OTP Field Component
First, import the `useOtpField` composable and use it in your OTP field component.
The `useOtpField` composable returns binding objects for the elements shown in the [anatomy](#anatomy). You will use `v-bind` to bind them to the corresponding DOM elements.
```vue
```
For your convenience, Formwerk already implements an OTP Slot component that you can use in your OTP field directly, but you can still build your own with `useOtpSlot`.
## Validation
### HTML Constraints
You can use the following properties to validate the OTP field with native HTML constraint validation:
| Name | Type | Description |
| ---------- | --------- | ----------------------------------- |
| `required` | `boolean` | Whether the text field is required. |
```vue
```
### Standard Schema
`useOtpField` also supports [Standard Schema](https://github.com/standard-schema/standard-schema) validation through the `schema` prop. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
```vue
```
### Mixed Validation
All OTP fields created with Formwerk support mixed validation, which means you can use both HTML constraints and Standard Schema validation to validate the field, and they work seamlessly together.
Note that HTML constraints are validated first, so any errors from the HTML constraints will be displayed first. Then, once all HTML constraints are satisfied, the Standard Schema is validated.
```vue
```
This makes schemas lighter; however, we recommend sticking to one or the other per form for maintainability.
If you need to disable the native validation, you can do so by setting the `disableHtmlValidation` prop to `true`.
## Usage
### Disabled
Use `disabled` to mark fields as non-interactive. Disabled fields are not validated and are not submitted.
If you need to prevent the user from interacting with the field while still allowing it to submit, consider using `readonly` instead.
```vue
```
### Readonly
Use `readonly` to mark fields as non-editable. Readonly fields are still validated and submitted.
```vue
```
### Accepting Specific Characters
The OTP field accepts a `accept` prop to specify the type of characters that can be entered.
You can have one of the following values:
| Value | Description |
| -------------- | --------------------------------------------------------------------------------------------------- |
| `alphanumeric` | Alphanumeric characters are accepted (i.e: English letters and numbers), this is the default value. |
| `numeric` | Only numeric characters are accepted. |
| `all` | All characters are accepted. |
```vue
```
### Masking Characters
The OTP field accepts a `mask` prop to specify the character to use for masking the entered characters, your model and submitted values be the actual values. The mask prop can either be a boolean which will apply a masked default, or a character that will be used to mask the entered value.
```vue
```
### Prefix
OTP fields can have a prefix, prefixes cannot be changed or edited by the user.
```vue
```
### Custom Length
OTP fields accept a `length` prop to specify the number of OTP slots. By default the length is 6 without a prefix, and with a prefix it will be 4.
```vue
```
### onCompleted Handler
The OTP field accepts an `onCompleted` handler to be notified when the user has filled all the OTP slots with valid characters.
```vue
```
### RTL
At this time, OTP fields do not support RTL (right-to-left) text direction. This is mainly because we want to get more feedback on this, from personal experience OTP codes are still LTR even in RTL web apps.
Feel free to open an issue on [GitHub](https://github.com/formwerkjs/formwerk/issues) if you have any feedback on this.
## API
### useOtpField
#### Props
These are the properties that can be passed to the `useOtpField` composable.
#### Returns
These are the properties in the object returned by the `useOtpField` composable.
### useOtpSlot
#### Props
These are the properties that can be passed to the `useOtpSlot` composable.
#### Returns
These are the properties in the object returned by the `useOtpSlot` composable.
---
--- title: Radio Buttons description: Learn how to build accessible Vue.js radio button components with Formwerk. ---
# Radio Buttons
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyRadio from '@components/AnatomyRadio.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import Kbd from '@components/KeyboardKey.vue';
import RadioGroupPartial from './_partials/_radioGroup.mdx';
import RadioInputPartial from './_partials/_radioInput.mdx';
import RadioItemPartial from './_partials/_radioItem.mdx';
> A radio group is a set of checkable buttons, known as radio buttons, where no more than one of the buttons can be checked at a time. Some implementations may initialize the set with all buttons in the unchecked state to force the user to check one of the buttons before moving past a certain point in the workflow.
Radios in HTML do not have a "group" concept, but they get grouped implicitly by the "name" attribute. This isn't the case in Vue and most UI libraries, as they are grouped by the model name they mutate.
Formwerk follows the "group" concept to provide a consistent API for radio fields regardless of whether they are bound to the same model or if they have the same name or not.
This means radios are a compound field, meaning they require more than one composable to work properly, and by extension, you need to build more than one component to make them work.
For radios, you will use the `useRadioGroup` and `useRadio` composables to build radio components.
## Features
You can build radio components using either the native HTML `input[type="radio"]` elements or other HTML elements. We provide the behavior, state, and accessibility implementation for both cases with the same API and features.
The following features are implemented:
- Support for either `input[type="radio"]` or custom HTML elements as a base element for the radio component.
- Labeling, descriptions, and error message displays are automatically linked to input and label elements with `aria-*` attributes.
- Form management, data collection, and validation with native HTML5 validation or [Standard Schema](https://github.com/standard-schema/standard-schema) validation.
- Support for orientation with `horizontal` and `vertical` values.
- `v-model` support for radio groups.
- Supported Keyboard features:
| Key | Description |
| -------------------------------------------- | -------------------------------------------------------------------------------- |
| | Focuses the next radio item in the group. |
| | Focuses the next radio item in the group. In RTL, focuses the previous item. |
| | Focuses the previous radio item in the group. |
| | Focuses the previous radio item in the group. In RTL, focuses the next item. |
| | Focuses the selected item in the group. If none selected, focuses the first one. |
| | Selects the focused radio item. |
## Anatomy
## Building a Radio Group Component
The `useRadioGroup` provides the behavior, state, and accessibility implementation for group components.
Unlike checkboxes, radio components **MUST** be grouped by a radio group component. This is why we will start by building a `RadioGroup` component as a prerequisite. We will be using this component in the following examples throughout this page.
## Building a Radio Component
With the Radio Group component built, we can now build a `RadioItem` component. You will be using the `useRadio` composable to build it.
You can use either the native HTML `input[type="radio"]` element or custom HTML elements. It doesn't matter which one you choose; both have the same exact API, and Formwerk does the work needed for each case behind the scenes.
```ts
import { type RadioProps, useRadio } from '@formwerk/core';
const props = defineProps();
const { labelProps, inputProps } = useRadio(props);
```
The most important part is to bind the `inputProps` object to the base element, the element that you consider to be the radio button. We also provide the `RadioProps` type for you to use as your component props. You are not required to use it, but it is recommended to make use of the full feature set of the `useRadio` composable and, by extension, your component.
With the basics out of the way, let's build a radio component with two common variations.
### With `input` element as a base
You can use the `useRadio` composable to build a radio component with the `input` element as a base.
```vue
```
The style-ability of the last example is limited to the styling capabilities of the native `input` element. To work around that, check the [styling section](#styling).
### With custom HTML element as a base
For unlimited styling freedom, you don't have to use the `input` element. With the same API, you can use custom HTML elements as a binding target for the `inputProps` object.
In the following example, we are using a `span` element as a base element for the radio. Try keyboard navigation, clicking, focusing, and other interactions to see how it behaves.
```vue
```
## Validation
Radio components support validation with native HTML5 constraints or [Standard Schema](https://github.com/standard-schema/standard-schema) validation. However, the `useRadioGroup` is the one that accepts validation props.
### HTML Constraint Validation
The following properties are supported on `useRadioGroup` and `useRadio` that use the `input` element as a base. Custom elements do not support these properties.
| Name | Type | Description |
| ---------- | --------- | ------------------------------------- |
| `required` | `boolean` | Whether the number field is required. |
```vue
```
### Standard Schema
`useRadioGroup` also supports [Standard Schema](https://github.com/standard-schema/standard-schema) validation through the `schema` prop. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
Here is an example of using zod as a Standard Schema to validate the radio group. We will be using the radio item component from the previous examples.
```vue
```
## Usage
### Disabled
You can disable individual radio items or the whole group with the `disabled` prop on either. Disabled radio items are not focusable. Disabled groups are not submitted and are not validated.
We made use of the styled radio component that we created above to make it clearer that the radio items are disabled.
```vue
```
If you need to prevent the user from interacting with the group while still allowing it to submit, consider using `readonly` instead.
### Readonly
Only available on the group, the `readonly` prop prevents the user from interacting with the group while still allowing it to submit and be validated.
```vue
```
### Orientation
Radio groups accept an `orientation` prop that can be set to `horizontal` or `vertical`. The orientation does not affect the focus order, but you can use it to layout the radio items in a row or column with CSS.
There is no default value assumed for the orientation, but if it is provided, the group element will have an `aria-orientation` attribute set to the value of the prop. So you can use that to style it.
```vue
```
### RTL
The radio group accepts a `dir` prop that can be set to `ltr` or `rtl`. Unlike the orientation, the `dir` prop affects the focus order of the radio items as the Left and Right arrow keys will navigate the items in the opposite direction.
```vue
```
## Radio button Value Type and Generics
By default, the value of a radio button is a string. You can change the value type by passing in the generic type to the `RadioProps` or `RadioGroupProps` types.
The following example shows how to change the value type to a string.
```ts
import { useRadio, type RadioProps } from '@formwerk/core';
const props = defineProps>();
// ...
```
But ideally, to make sure your component is type-safe and as flexible as possible, you should use generics on the component itself.
```vue
```
You can do the same for the `RadioGroup` components as well.
```vue
```
## API
Most of the values expressed below are wrapped in `Ref` as they are reactive values.
### Radio Group
#### Props
These are the properties that can be passed to the `useRadioGroup` composable.
#### Returns
These are the properties in the object returned by the `useRadioGroup` composable.
### Radio
#### Props
These are the properties that can be passed to the `useRadio` composable.
#### Returns
These are the properties in the object returned by the `useRadio` composable.
---
--- title: Search Fields description: Learn how to build accessible search field Vue components with Formwerk. ---
# Search Fields
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomySearchField from '@components/AnatomySearchField.vue';
import Kbd from '@components/KeyboardKey.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import SearchFieldPartial from './_partials/_searchField.mdx';
> Input elements of type search are text fields designed for the user to enter search queries into. These are functionally identical to text inputs but may be styled differently by the user agent.
Search fields have extra behaviors and use-cases that set them apart from regular text fields. This composable provides the behavior, state, and accessibility implementation for search fields.
A couple of behaviors set this apart from regular text fields:
- The text content can be cleared with the clear button or a keyboard shortcut.
- They are usually used without a parent `form` element and sometimes without a `submit` button. So they can be submitted with the `Enter` keyboard key on their own.
You can find more information about the differences [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/search#differences_between_search_and_text_types).
## Features
- Uses `input[type="search"]` element as a base.
- Labeling, descriptions, and error message displays are automatically linked to input and label elements with `aria-*` attributes.
- Support for a custom clear button.
- Validation support with native HTML constraint validation or [Standard Schema](https://github.com/standard-schema/standard-schema) validation.
- Support for `v-model` binding.
- Supported Keyboard features:
| Key | Description |
| --------------------------------- | --------------------------------------------------------------------------------------------------- |
| | Clears the input value. |
| | If in a form, submits the form. Otherwise, submits the input with the `onSubmit` event or callback. |
## Anatomy
## Building a Search Field Component
The `useSearchField` composable returns binding objects for the elements shown in the [anatomy](#anatomy). You will use `v-bind` to bind them to the corresponding DOM elements.
```vue
Search value: {{ search }}
```
Notice that we imported the `SearchFieldProps` in the previous example. This is recommended to use as your component prop types. Not only do you get type safety for your component out of it, but it also handles the reactivity aspects of the props so you don't have to. You are free to extend it with your own props or omit the ones you don't need.
## Validation
### HTML Constraints
You can use the following native HTML validation properties to validate the search field:
| Name | Type | Description |
| ----------- | ------------------ | ------------------------------------ |
| `maxLength` | `number` | The maximum length of characters. |
| `minLength` | `number` | The minimum length of characters. |
| `required` | `boolean` | Whether the text field is required. |
| `pattern` | `string \| RegExp` | A regular expression for validation. |
Here is an example of how to use the `maxLength` and `minLength` properties to limit the text length between 3 and 18 characters.
Assuming you have a `SearchField` component like the one shown above, you can use it like this:
```vue
```
### Standard Schema
`useSearchField` also supports [Standard Schema](https://github.com/standard-schema/standard-schema) validation through the `schema` prop. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
```vue
```
### Mixed Validation
All search fields created with Formwerk support mixed validation, which means you can use both HTML constraints and Standard Schemas to validate the field, and they work seamlessly together.
Note that HTML constraints are validated first, so any errors from the HTML constraints will be displayed first. Then, once all HTML constraints are satisfied, the Standard Schema is validated.
```vue
```
This makes schemas lighter; however, we recommend sticking to one or the other per form for maintainability.
If you need to disable the native validation, you can do so by setting the `disableHtmlValidation` prop to `true`.
```vue
```
You can also disable it globally for all fields. For more information, check out the [Validation](/guides/forms/validation/) guide.
## Usage
### Disabled
You can disable the search field by setting the `disabled` prop to `true`. Disabled fields are not editable, will not be validated, and will not be submitted with the form.
If you need to prevent the user from interacting with the field while still allowing it to submit, consider using `readonly` instead.
```vue
```
### Readonly
Readonly fields are validated and submitted, but they do not accept user input. The field is still focusable, and the value is copyable. For more info, check the [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly).
```vue
```
### RTL
The search field doesn't really need much for RTL support; however, the `dir` prop can be used to set the direction of the field for convenience.
```vue
```
### Submitting
Search fields can be used without a parent `form` element and sometimes without a `submit` button. The `useSearchField` accepts an `onSubmit` callback that is called when the user presses the `Enter` key on the field.
```vue
```
Note that empty search fields can be submitted, so you might want to validate the field before submitting.
## API
### Props
These are the properties that can be passed to the `useSearchField` composable.
### Returns
These are the properties in the object returned by the `useSearchField` composable.
---
--- title: Select Fields description: Learn how to build accessible select field Vue components with Formwerk. ---
# Select Fields
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomySelect from '@components/AnatomySelect.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import Kbd from '@components/KeyboardKey.vue';
import OptionPartial from './_partials/_option.mdx';
import OptionGroupPartial from './_partials/_optionGroup.mdx';
import SelectPartial from './_partials/_select.mdx';
Select fields are very common form fields. They allow the user to select one or more options from a list of options.
The native `select` element does a lot in terms of interactivity and accessibility. However, it leaves a lot to be desired in terms of styling and customization. Formwerk tries to address that by providing the same accessability and interactive behaviors to your custom select component so you don't have to compromise for the sake of styling.
Note that [customizable selects](https://developer.chrome.com/blog/rfc-customizable-select) are coming soon to the browser and Formwerk will leverage that when it becomes available.
## Features
- Labeling, descriptions, error message displays are automatically linked to input and label elements with `aria-*` attributes.
- Support for single/multiple selections.
- Support for option groups.
- Support for option searching with starting characters.
- First-class Support for `[popover]` popups for dropdowns/menus.
- Generic typing support for options.
- Validation support with native HTML constraints or [Standard Schemas](https://github.com/standard-schema/standard-schema).
- Support for `v-model` binding.
- Supported Keyboard features:
When focusing the trigger element, and menu is closed:
| Key | Description |
| ------------------------------------------ | ----------------------- |
| | Opens the options menu. |
| | Opens the options menu. |
| | Opens the options menu. |
| | Opens the options menu. |
When focusing an option in the menu, and menu is open:
| Key | Description |
| -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| | Selects the option, if multiple toggles the option selection state. |
| | Selects the option, if multiple toggles the option selection state. |
| | Focuses the next option if available. |
| | Focuses the previous option if available. |
| + | Focuses the next option and selects it. |
| + | Focuses the previous option and selects it. |
| | Focuses the first option. |
| + | Focuses the first option. If multiple is enabled, selects all options between the last focused option and the first option. |
| | Focuses the first option. |
| + | Focuses the first option. If multiple is enabled, selects all options between the last focused option and the first option. |
| | Focuses the last option. |
| + | Focuses the last option. If multiple is enabled, selects all options between the last focused option and the last option. |
| | Focuses the last option. |
| + | Focuses the last option. If multiple is enabled, selects all options between the last focused option and the last option. |
| + | If multiple is enabled, selects all options. If all are already selected, deselects all options. |
Most people aren't aware of these keyboard shortcuts, but they do make a big difference in the user experience for keyboard users.
## Anatomy
## Building a Select Field
The select field is one of the most complex fields with multiple moving parts. It is what Formwerk considers a "compound component" because it is made up of an ecosystem of components that work together to create a single field.
From the anatomy diagram above, you can see that the core select field is made up of the following parts:
- **Trigger:** A trigger element that is used to open the options menu, doubles as the selected value display.
- **Listbox:** An options menu that contains the list of options.
- **Option:** An option component that represents each option in the list.
- **Option Group:** Not illustrated above, an option group that groups options together based on some categorization.
Formwerk handles all of these parts and abstracts them into the following component ecosystem:
- **Select**: Contains the trigger and listbox parts. You will use `useSelect` to build it.
- **Option**: Represents an option in the list. You will use `useOption` to build it.
- **OptionGroup**: Represents a group of options in the list. You will use `useOptionGroup` to build it.
Notice that the listbox is not a separate component. This is because popups today can be done in different ways, so Formwerk while offers the open state along with some accessability attributes, it does not have a specific listbox component implementation. But we offer out of the box support for [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API) if you happen to use one for the listbox element.
### Building an Option Component
We can start by using `useOption` to create our option component. This component will be responsible for rendering each option in the list.
Notice that we are using the `OptionProps` type to define the props for our component. This type is generic and allows you to specify the type of the option value. In this case, we are using `any` to represent any type of value.
The `label` is available inside `OptionProps` and we will display it to the user.
### Building a Select Component
Next, you will use `useSelect` to create our select component. This component will be responsible for rendering the trigger and listbox parts.
The `useSelect` composable returns binding objects for some of the elements shown in the [anatomy](#anatomy), you will use `v-bind` to bind them to the corresponding DOM elements.
```vue
```
### Building an Option Group Component
Building an option group component is similar to building an option component. You will use `useOptionGroup` to create the component.
```vue
```
## Validation
### HTML Constraints
You can use the following properties to validate the date field with native HTML constraint validation:
| Name | Type | Description |
| ---------- | --------- | ------------------------------------- |
| `required` | `boolean` | Whether the select field is required. |
```vue
```
### Standard Schema
`useSelect` supports [Standard Schema](https://github.com/standard-schema/standard-schema) validation through the `schema` prop. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
```vue
```
### Mixed Validation
All select fields created with Formwerk support mixed validation, which means you can use both HTML constraints and Standard Schema validation to validate the field, and they work seamlessly together.
Note that HTML constraints are validated first, so any errors from the HTML constraints will be displayed first. Then, once all HTML constraints are satisfied, the Standard Schema is validated.
```vue
```
## Usage
### Multiple Select
The `useSelect` composable accepts a `multiple` prop, it adjusts behaviors to what users expect out of a multi-select field.
```vue
```
### Disabled
Use disabled to mark fields as non-interactive, disabled fields are not validated and are not submitted.
If you need to prevent the user from interacting with the field while still allowing it to submit, consider using `readonly` instead.
```vue
```
You can also mark individual options as disabled by passing a `disabled` prop to the `OptionItem` component. Note that disabled options are skipped from the focus order when using shortcuts or the search functionality.
```vue
```
### Readonly
Readonly fields are validated and submitted, but they do not accept user input. The field is still focusable and the popup is still openable. For more info, check the [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly).
```vue
```
## Styling
When styling the select field, you need to be aware of the following...
#### Option attributes
When an option is selected, it will receive the `aria-selected="true"` attribute if the select field is a single choice select.
If the select field is a multi-choice select, the selected options will receive the `aria-checked="true"` attribute instead.
#### Trigger attributes
The trigger element will receive the `aria-expanded` attribute. So you can style the trigger according to the popover state if needed.
It will also receive the `aria-activedescendant` attribute when an option is focused.
#### ListBox attributes
The listbox element will receive the `aria-multiselectable` attribute if the select field is a multi-choice select.
## API
### Option
#### Props
These are the properties that can be passed to the `useOption` composable.
#### Returns
These are the properties in the object returned by the `useOption` composable.
### Option Group
#### Props
These are the properties that can be passed to the `useOptionGroup` composable.
#### Returns
These are the properties in the object returned by the `useOptionGroup` composable.
### Select
#### Props
These are the properties that can be passed to the `useSelect` composable.
#### Returns
These are the properties in the object returned by the `useSelect` composable.
---
--- title: Sliders description: Learn how to build accessible slider Vue components with Formwerk. ---
# Sliders
import AnatomySlider from '@components/AnatomySlider.vue';
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import Kbd from '@components/KeyboardKey.vue';
import PreviewCard from '@components/PreviewCard.vue';
import SliderThumb from './_partials/_sliderThumb.mdx';
import Slider from './_partials/_slider.mdx';
import SliderMultiple from './_partials/_sliderMultiple.mdx';
> A slider is an input where the user selects a value from within a given range. Sliders typically have a slider thumb that can be moved along a bar, rail, or track to change the value of the slider.
## Features
- Labeling, descriptions, and error message displays are automatically linked to input and label elements with `aria-*` attributes.
- `v-model` support for binding the value of the slider and the individual thumbs.
- Multi-thumb support with auto value clamping.
- Support for `min`, `max`, and `step` attributes.
- Support for both horizontal and vertical orientations.
- Support for both LTR and RTL directions.
- Validation with [Standard Schema](https://github.com/standard-schema/standard-schema).
- Interactive behaviors:
- Clicking the track element sets the value of the slider or the nearest suitable thumb to the clicked position.
- Dragging the thumb element changes the value of the slider.
- Supported keyboard interactions:
| Key | Description |
| ----------------------------------------- | ------------------------------------------------------------------------------------------ |
| | Increments the slider value of the currently focused thumb. In RTL, it decrements instead. |
| | Decrements the slider value of the currently focused thumb. In RTL, it increments instead. |
| | Increments the slider value of the currently focused thumb. |
| | Decrements the slider value of the currently focused thumb. |
| | Sets the slider value of the currently focused thumb to the minimum possible value. |
| | Sets the slider value of the currently focused thumb to the maximum possible value. |
| | Increments the slider value of the currently focused thumb by a large step. |
| | Decrements the slider value of the currently focused thumb by a large step. |
## Anatomy
## Building a thumb component
Every slider needs at least one thumb to represent the current value of the slider. This means the slider in Formwerk is a compound component similar to radio buttons.
First, let's build a thumb component that will be used in the slider later. We will keep styling to a minimum by using a simple circle SVG.
You will be using the `useSliderThumb` composable to build the thumb component.
There is nothing to show yet because we need to build the slider component.
## Building a slider component
You will be using the `useSlider` composable to build the slider component.
The `useSlider` composable returns binding objects for the elements shown in the [anatomy](#anatomy). You will use `v-bind` to bind them to the corresponding DOM elements.
The Slider is what we consider a fully custom component, meaning it doesn't have an underlying `input` base element that you can use. While the `input[type="range"]` may be a suitable candidate, it doesn't scale to support the wide range (pun intended) of use-cases that developers expect of slider inputs today.
```vue
```
Notice that in order to model the slider progress visually, we used the utility composable `useThumbMetadata` to calculate the percentage of the thumb position.
## Validation
Because sliders in Formwerk are a fully custom component, they don't support any HTML validation attributes. You can, however, use [Standard Schema](https://github.com/standard-schema/standard-schema) to validate the value of the slider.
### Standard Schema
The `useSlider` composable accepts a `schema` prop that is an instance of a Standard Schema. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
```vue
```
## Usage
### Multiple Thumbs
You can have as many thumbs as you want inside a slider. Just add another `` component, and this will be automatically handled for you:
- Min/Max clamping for each thumb.
- Value conversion to an array instead of a single number.
```vue
MultipleSlider value is: {{ value }}
```
Note that we did change some CSS from the previous examples in order to color the track properly, but it is up to you how you want to do that in any way you want.
### Disabled
Use `disabled` to mark sliders as non-interactive. Disabled sliders are not validated and are not submitted.
```vue
```
You can also disable any individual `Thumb` by passing `disabled` to it as well. But it won't prevent validation/submission of the slider.
### Readonly
Readonly sliders are validated and submitted, but they do not accept user input. The slider thumbs would still be focusable. For more info, check the [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly).
```vue
```
### RTL
The `useSlider` composable accepts a `dir` property. You can set it to `RTL`, and it will handle thumb positioning automatically along with inverting the horizontal arrow keys (left/right arrows).
```vue
```
### Orientation
`useSlider` also accepts an `orientation` prop. You can set it to either `horizontal` (default) or `vertical`.
Formwerk will handle most things in either orientation in terms of interaction and thumb positioning. But slider layout is left to you to do.
Here is an example that uses the `[aria-orientation]` attribute that is applied automatically. We will use it to flip the slider we initially created with some custom styles.
```vue
```
### Discrete Slider Values
You can also use discrete slider values by passing an `options` prop to the slider. This is useful for non-numeric values like a rating slider.
When doing this, the slider min/max will be set to the first and last option, and the step will be set to the difference between the first and last option divided by the number of options you want to show. This also means the `step` prop is ignored when `options` is used.
```vue
```
### Generic Types
The slider's `SliderProps` accepts a generic type parameter that allows you to build generically typed slider components.
```vue
```
This gives you auto type inference for the slider's value even when using the `options` prop for any type of value.
## Styling
You probably have noticed that the `trackProps` and `thumbProps` contain some minor styling properties. These are the bare minimum to get the slider working, and they are automatically added for you.
If you are interested in knowing what properties are added, here is a list:
- `trackProps` style properties:
- `container-type`: is set to `size` or `inline-size` depending on the orientation. You should NOT override this property.
- `position`: is set to `relative`. You can override this to anything but `static`.
- `thumbProps` style properties:
- `position`: is set to `absolute`. You should NOT override this property.
- `translate`: used to position the thumb. You should NOT override this property.
- `will-change`: is set to `translate`. You should NOT override this property.
- Any inset position properties like `top`, `left`, `right`, `bottom` are set to `0`. You should NOT override these properties.
We were very careful to not add any easily overridden properties to the `trackProps` and `thumbProps` to avoid any conflicts with your custom styles.
Other than all of that, you can use any styling solution you want, whether it's [TailwindCSS](https://tailwindcss.com/) or plain CSS.
#### Thumb attributes
The thumb element will receive the `aria-orientation` attribute, which is the same as the slider's orientation, so you can style tooltips or other UI elements according to the slider's orientation.
#### Slider attributes
The slider element will receive the `aria-orientation` attribute, which is the prop you pass to the slider.
## Examples
## API
### Thumb
#### Props
These are the properties that can be passed to the `useSliderThumb` composable.
#### Returns
These are the properties in the object returned by the `useSliderThumb` composable.
### Slider
#### Props
These are the properties that can be passed to the `useSlider` composable.
#### Returns
These are the properties in the object returned by the `useSlider` composable.
#### `useThumbMetadata` returns
Additionally, `useThumbMetadata` returns a computed object with the following properties:
---
--- title: Switches description: Learn how to build accessible switch Vue components with Formwerk. ---
# Switches
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomySwitch from '@components/AnatomySwitch.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import Kbd from '@components/KeyboardKey.vue';
import SwitchPartial from './_partials/_switch.mdx';
import SwitchCustomPartial from './_partials/_switchCustom.mdx';
> A switch is an input widget that allows users to choose one of two values: on or off. Switches are similar to checkboxes and toggle buttons, which can also serve as binary inputs. One difference, however, is that switches can only be used for binary input.
The switch field has enough unique behaviors and use-cases that justify it having its own composable. The binary state of a switch means it shouldn't be used to represent "required" inputs where the switch needs to be "on". It is a user preference that can be turned off.
## Features
- You can use an `input[type="checkbox"]` element as a base or any custom element.
- Labeling, descriptions, and error message displays are automatically linked to input and label elements with `aria-*` attributes.
- Support for custom `on` and `off` values.
- Validation support with native HTML constraint validation or [Standard Schema](https://github.com/standard-schema/standard-schema) validation.
- Support for `v-model` binding.
- Supported keyboard features:
| Key | Description |
| ---------------------------------- | -------------------------- |
| | Toggles the switch on/off. |
| | Toggles the switch on/off. |
## Anatomy
## Building a Switch component
The `useSwitch` composable provides the necessary props and methods to build a switch component. It returns binding objects for the elements shown in the [anatomy](#anatomy). You will use `v-bind` to bind them to the corresponding DOM elements.
There are two ways to build a switch component:
- With an `input[type="checkbox"]` as a base element.
- Without an `input` element, like a `div` or a `button`.
We will review the two ways to build a switch component in the following examples.
### With `input[type="checkbox"]` base element
We will add some styling to the next example because switches don't look like one unless we style them. Otherwise, it would just look like a checkbox.
```vue
Switch is: {{ isOn }}
```
### Without input elements
Similar to the previous example, we can achieve the same look and behavior with a `div` element or a `button` element.
In this example, we will use an `svg` element to closely resemble a switch. We will borrow those SVG paths from [Phosphor Icons](https://phosphoricons.com/).
```vue
Switch is: {{ isOn }}
```
## Validation
### HTML Constraints
While ideally, the switch field should not be validatable, you can still use the `required` attribute if you choose to use an `input[type="checkbox"]` as a base element. We make no assumptions about how you want to use the switch field.
| Name | Type | Description |
| ---------- | --------- | ------------------------------------- |
| `required` | `boolean` | Whether the number field is required. |
```vue
```
### Standard Schemas
`useSwitch` also supports [Standard Schema](https://github.com/standard-schema/standard-schema) validation through the `schema` prop. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
```vue
```
### Mixed Validation
While it is unlikely that you need both HTML constraints and Standard Schemas to validate a switch, Formwerk supports mixed validation, which means you can use both HTML constraints and Standard Schemas to validate the switch, and they will work together.
Note that HTML constraints are validated first, so any errors from the HTML constraints will be displayed first. Then, once all HTML constraints are satisfied, the Standard Schema is validated.
```vue
```
If you need to disable the native validation, you can do so by setting the `disableHtmlValidation` prop to `true`. You can also disable it globally for all fields. For more information, check out the [Validation](/guides/forms/validation/) guide.
```vue
```
## Usage
### Disabled
Use `disabled` to mark the switch as non-interactive. Disabled switches are not validated and will not be submitted.
If you need to prevent the user from interacting with the field while still allowing it to submit, consider using `readonly` instead.
```vue
```
### Readonly
Readonly switches are validated and submitted, but they do not accept user input. The switch is still focusable, and the value is copyable. For more info, check the [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly).
```vue
```
### Custom On/Off values
You can customize the `on` and `off` values of the switch component by passing the `trueValue` and `falseValue` props. They can be anything you want.
```vue
Value is: {{ value }}
```
## API
### Props
These are the properties that can be passed to the `useSwitch` composable.
### Returns
These are the properties in the object returned by the `useSwitch` composable.
---
--- title: Text Fields description: Learn how to build accessible text field Vue components with Formwerk. ---
# Text Fields
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import AnatomyTextField from '@components/AnatomyTextField.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import TextFieldPartial from './_partials/_textField.mdx';
import TextAreaPartial from './_partials/_textarea.mdx';
Text fields are used to allow users to input plain text into a form.
Text fields are implemented with the `input` element for a single line of text or the `textarea` element for multiple lines of text.
## Features
- Uses `input` or `textarea` elements as a base.
- Labels, descriptions, and error message displays are automatically linked to input and label elements with `aria-*` attributes.
- Validation support with native HTML constraint validation or [Standard Schema](https://github.com/standard-schema/standard-schema) validation.
- Support for `v-model` binding.
## Anatomy
## Building a Text Field Component
You can start by importing the `useTextField` composable and using it in your text field component.
The `useTextField` composable returns binding objects for the elements shown in the [anatomy](#anatomy). You will use `v-bind` to bind them to the corresponding DOM elements.
```vue
```
Notice that we imported the `TextFieldProps` in the previous example. This is recommended to use as your component prop types.
Not only do you get type safety for your component out of it, but it also handles the reactivity aspects of the props so you don't have to. You are free to extend it with your own props or omit the ones you don't need.
## Building a Text Area Component
Instead of using an `input[type="text"]` element, you can switch to using a `textarea` element as a base element instead.
```vue
```
## Validation
### HTML Constraints
You can use the following properties to validate the text field with native HTML constraint validation:
| Name | Type | Description |
| ----------- | ------------------ | ------------------------------------------------------------------------- |
| `maxLength` | `number` | The maximum length of characters. |
| `minLength` | `number` | The minimum length of characters. |
| `required` | `boolean` | Whether the text field is required. |
| `pattern` | `string \| RegExp` | A regular expression for validation. Not supported for `textarea` fields. |
In addition to the above properties, if you are using the `input` element, you can use the built-in validation for the `type` attribute.
| Type | Description |
| ------- | -------------------------------- |
| `email` | Validates the value as an email. |
| `url` | Validates the value as a URL. |
Here is an example of how to use the `maxLength` and `minLength` properties to limit the text length between 3 and 18 characters.
Assuming you have a `TextField` component like the one shown above, you can use it like this:
```vue
```
### Standard Schema
`useTextField` also supports [Standard Schema](https://github.com/standard-schema/standard-schema) validation through the `schema` prop. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
```vue
```
### Mixed Validation
All text fields created with Formwerk support mixed validation, which means you can use both HTML constraints and Standard Schema validation to validate the field, and they work seamlessly together.
Note that HTML constraints are validated first, so any errors from the HTML constraints will be displayed first. Then, once all HTML constraints are satisfied, the Standard Schema is validated.
```vue
```
This makes schemas lighter; however, we recommend sticking to one or the other per form for maintainability.
If you need to disable the native validation, you can do so by setting the `disableHtmlValidation` prop to `true`.
```vue
```
You can also disable it globally for all fields. For more information, check out the [Validation](/guides/forms/validation/) guide.
## Usage
### Disabled
Use `disabled` to mark fields as non-interactive. Disabled fields are not validated and are not submitted.
If you need to prevent the user from interacting with the field while still allowing it to submit, consider using `readonly` instead.
```vue
```
### Readonly
Readonly fields are validated and submitted, but they do not accept user input. The field is still focusable, and the value is copyable. For more info, check the [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly).
```vue
```
### RTL
The text field doesn't require much for RTL support; however, the `dir` prop can be used to set the direction of the field for convenience.
```vue
```
## Styling
Formwerk does not come with any markup or styling, which is often the part that makes a design system unique. That means you can use any styling solution you want, whether it's [TailwindCSS](https://tailwindcss.com/) or plain CSS, as you have already seen.
Note that we make use of the `:user-invalid` pseudo-classes to style the field and control when to show the error messages without any JavaScript. Formwerk leans into native APIs and browser features to provide a more seamless experience for the user. In fact, even if you use Standard Schemas to validate the field, the pseudo-classes will still work.
For more information on styling and recommendations, check the [Styling](/extras/styling/) guide.
## API
### Props
These are the properties that can be passed to the `useTextField` composable.
### Returns
These are the properties in the object returned by the `useTextField` composable.
---
--- title: Time Fields description: Learn how to build accessible time field Vue components with Formwerk. ---
# Time Fields
import Kbd from '@components/KeyboardKey.vue';
import AnatomyTimeField from '@components/AnatomyTimeField.vue';
import AnatomyCanvas from '@components/AnatomyCanvas.vue';
import MdxRepl from '@components/MdxRepl.vue';
import MdxTableAPI from '@components/MdxTableAPI.vue';
import TimeFieldPartial from './_partials/_timeField.mdx';
import PreviewCard from '@components/PreviewCard.vue';
Time fields are a type of input field that allows users to enter a time, usually in the format of `hh:mm`.
## Features
- Labeling, Descriptions, and error messages are automatically linked to the time field elements.
- Validation support with native HTML constraints or [Standard Schemas](https://github.com/standard-schema/standard-schema).
- Support for `v-model` binding.
- Comprehensive keyboard shortcuts support.
- Focus management and auto navigation for time segments.
### Keyboard Features
| Key | Description |
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| | Decrements selected segment by 1. |
| | Increments selected segment by 1. |
| | Moves the focus to the previous segment. |
| | Moves the focus to the next segment. |
| | Clears the current segment. |
| | Clears the current segment. |
| | Moves the focus to the next segment or next element in the tab index order if it is the last segment. |
## Anatomy
## Building a Time Field Component
Just like the [native HTML time field](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/time), the time field in Formwerk is comprised of time segments, each segment represents a part of the time value (e.g: hour, minute).
The segments are generated for you automatically based on the `formatOptions` you pass to the `useTimeField` composable. You will need to bind the prop objects returned by the composable to the corresponding DOM elements in the [anatomy](#anatomy).
If you do not pass the `formatOptions` prop, the time field will use an `HH:mm` format.
```vue
```
## Validation
### HTML Constraints
You can use the following properties to validate the time field with native HTML constraint validation:
| Name | Type | Description |
| ---------- | --------- | ------------------------------------- |
| `min` | `Date` | The minimum time that can be entered. |
| `max` | `Date` | The maximum time that can be entered. |
| `required` | `boolean` | Whether the time field is required. |
```vue
```
### Standard Schema
`useTimeField` supports [Standard Schema](https://github.com/standard-schema/standard-schema) validation through the `schema` prop. This includes multiple providers like [Zod](https://zod.dev/), [Valibot](https://valibot.dev/), [Arktype](https://arktype.dev/), and more.
In this example, we are validating that the time field is between 00:00 and 10:04.
```vue
```
### Mixed Validation
All time fields created with Formwerk support mixed validation, which means you can use both HTML constraints and Standard Schema validation to validate the field, and they work seamlessly together.
```vue
```
## Usage
### Disabled
Use the `disabled` prop to disable the time field. Disabled time fields are not validated and are not submitted.
```vue
```
### Readonly
Use the `readonly` prop to make the time field read-only. Read-only time fields are still validated and submitted.
If you need to prevent the user from interacting with the field while still allowing it to submit, consider using `readonly` instead.
```vue
```
### Min and Max
You can pass `min` and `max` props to set the minimum and maximum times that can be entered.
```vue
```
### Format Options
You can pass a partial of the `Intl.DateTimeFormatOptions` to the `formatOptions` prop to customize the time field's display format. For example, we can switch to 12-hour format.
```vue
```
### Show Seconds
Not only you can change the 12-hour format to 24-hour format, you can also control the visibility of the seconds segment with the `formatOptions` as well.
```vue
```
## Examples
These are some examples of time fields built with Formwerk.
## API
### useTimeField
#### Props
These are the properties that can be passed to the `useTimeField` composable.
#### Returns
These are the properties in the object returned by the `useTimeField` composable.
### useDateSegment
#### Props
These are the properties that can be passed to the `useDateSegment` composable.
#### Returns
These are the properties in the object returned by the `useDateTimeSegment` composable.
---