Skip to content

Commit

Permalink
docs: documentation for v0.4 (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
edmundhung authored Oct 29, 2022
1 parent 1de23e0 commit 91e4c3e
Show file tree
Hide file tree
Showing 45 changed files with 1,360 additions and 1,076 deletions.
29 changes: 11 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,29 @@

Conform is a form validation library built on top of the [Constraint Validation](https://caniuse.com/constraint-validation) API.

- **Progressive Enhancement**: It is designed based on the [HTML specification](https://html.spec.whatwg.org/dev/form-control-infrastructure.html#the-constraint-validation-api). From validating the form to reporting error messages for each field, if you don't like part of the solution, just replace it with your own.
- **Framework Agnostic**: The DOM is the only dependency. Conform makes use of native [Web APIs](https://developer.mozilla.org/en-US/docs/Web/API) exclusively. You don't have to use React / Vue / Svelte to utilise this library.
- **Flexible Setup**: It can validates fields anywhere in the dom with the help of [form attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#form). Also enables CSS pseudo-classes like `:valid` and `:invalid`, allowing flexible styling across your form without the need to manipulate the class names.
- **Progressive Enhancement**: Its APIs are designed with progressive enhancement in mind to ensure a smooth and resillent experience before javascript is ready.
- **Server-first validation**: It simplifies the mental model by utilizing a server-first validation flow which submits your form for validation.
- **Lightweight**: It is only [4kB](https://bundlephobia.com/package/@conform-to/react) compressed thanks to all the native [Web APIs](https://developer.mozilla.org/en-US/docs/Web/API). _#useThePlatform_

## Quick start

<!-- sandbox title="Simple demo" src="/examples/basic" -->
<!-- sandbox src="/examples/basic" -->

```tsx
import { useForm, useFieldset } from '@conform-to/react';

export default function LoginForm() {
const form = useForm({
onSubmit(event, { submission }) {
onSubmit(event) {
event.preventDefault();

console.log(submission);
const formData = new FormData(event.currentTarget);
const value = Object.fromEntries(formData);

console.log(value);
},
});
const { email, password } = useFieldset(form.ref);
const { email, password } = useFieldset(form.ref, form.config);

return (
<form {...form.props}>
Expand All @@ -41,16 +44,6 @@ export default function LoginForm() {
}
```

Learn more about conform [here](https://conform.guide/basics)
Learn more about conform [here](https://conform.guide/basics).

<!-- /sandbox -->

## API References

<!-- prettier-ignore-start -->
| Package | Description | Size |
| :------ | :---------- | :--- |
| [@conform-to/react](packages/conform-react) | View adapter for [react](https://github.com/facebook/react) | [![package size](https://img.shields.io/bundlephobia/minzip/@conform-to/react)](https://bundlephobia.com/package/@conform-to/react) |
| [@conform-to/yup](packages/conform-yup) | Schema resolver for [yup](https://github.com/jquense/yup) | [![package size](https://img.shields.io/bundlephobia/minzip/@conform-to/yup)](https://bundlephobia.com/package/@conform-to/yup) |
| [@conform-to/zod](packages/conform-zod) | Schema resolver for [zod](https://github.com/colinhacks/zod) | [![package size](https://img.shields.io/bundlephobia/minzip/@conform-to/zod)](https://bundlephobia.com/package/@conform-to/zod) |
<!-- prettier-ignore-end -->
147 changes: 118 additions & 29 deletions docs/basics.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# Basics

In this section, we will cover how to build a simple login form by utilizing native constraint and then enhancing it with conform.
In this section, we will show you how to build a login form by utilizing the **Constraint Validation** API with **Conform**.

<!-- aside -->

## Table of Contents

- [Installation](#installation)
- [Quick start](#quick-start)
- [Constraint Validation](#constraint-validation)
- [Capturing errors](#capturing-errors)
- [Styling input](#styling-input)
- [Customize messages](#customize-messages)
- [Early reporting](#early-reporting)
- [Demo](#demo)

Expand All @@ -25,7 +26,7 @@ npm install @conform-to/react

To begin, let's make a login form with 2 basic requirements:

- The **email** field should be a valid email address
- The **email** field should contain a valid email address
- The **password** field should not be empty

```tsx
Expand All @@ -36,7 +37,8 @@ export default function LoginForm() {
event.preventDefault();

const formData = new FormData(event.currentTarget);
console.log(formData);

console.log(Object.fromEntries(formData));
}}
>
<label>
Expand All @@ -59,77 +61,164 @@ export default function LoginForm() {
}
```

By utilising the [required](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/required) attribute, our form now stop users from submitting until they provide a valid email address with the password. We are also capturing the form value using the [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) API.
Both the **email** input type and the [required](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/required) attribute are parts of the [Constraint Validation](#constraint-validation) API. It tells the browser to stop users from submitting the form until they provide a valid email address with the password.

### Constraint Validation

The [Constraint Validation](https://caniuse.com/constraint-validation) API is introduced with HTML5 to enable native client side form validation. This includes:

- Utilizing [HTML attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Constraint_validation#validation-related_attributes) for validations (e.g. `required`, `type`)
- Accessing form validity and configuring custom constraint through the [DOM APIs](https://developer.mozilla.org/en-US/docs/Web/API/Constraint_validation#extensions_to_other_interfaces) (e.g `validityState`, `setCustomValidity()`)
- Styling form elements with [CSS pseudo-class](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#the_constraint_validation_api) based on the validity (e.g. `:required`, `:invalid`)

Conform utilizes these APIs internally. For example, form errors are reported by listening to the [invalid event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/invalid_event) and the error messages are captured from the element [validationMessage](https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validationMessage) property.

### Capturing errors

With the help of native form validation, users will see [error bubbles](https://codesandbox.io/s/cocky-fermi-zwjort?file=/src/App.js) popping up if they try to submit a form with invalid fields. These bubbles, unfortunately, are not customizable.
With the help of Constraint Validation, users will see [error bubbles](https://codesandbox.io/s/cocky-fermi-zwjort?file=/src/App.js) popping up when they try to submit a form with invalid fields. These bubbles are not customizable unfortunately. What if we can capture the error messages and decide where to put them ourselves?

What if we can capture the error messages and decide where to put them ourselves? Let's introduce the [useForm](/packages/conform-react/README.md#useform) and [useFieldset](/packages/conform-react/README.md#usefieldset) hooks:
Let's introduce the [useForm](/packages/conform-react/README.md#useform) and [useFieldset](/packages/conform-react/README.md#usefieldset) hooks.

```tsx
import { useForm, useFieldset } from '@conform-to/react';

export default function LoginForm() {
/**
* The useForm hook let you take control of the browser
* validation flow and customize it
* validation flow and customize it. The submit event
* handler will be called only when the form is valid.
*/
const form = useForm({
onSubmit(event, { formData }) {
event.preventDefault();

console.log(formData);
console.log(Object.fromEntries(formData));
},
});

/**
* The useFieldset hook let you subscribe to the state
* of each field
* The useFieldset hook helps you configure each field and
* subscribe to its state. The properties accessed should
* match the name of the inputs.
*/
const { email, password } = useFieldset(form.ref);
const { email, password } = useFieldset(form.ref, form.config);

return (
<form {...form.props}>
{/* ... */}
<label>
<div>Email</div>
<input type="email" name="email" required autoComplete="off" />
{/* The email error captured */}
<div>{email.error}</div>
</label>
<label>
<div>Password</div>
<input type="password" name="password" required />
{/* The password error captured */}
<div>{password.error}</div>
</label>
<label>
<div>
<span>Remember me</span>
<input type="checkbox" name="remember-me" value="yes" />
</div>
</label>
<button type="submit">Login</button>
</form>
);
}
```

<details>
<summary>Where are these error messages come from?</summary>
You might already notice - they are the same as the one you saw on the error bubbles: Indeed, these messages are provided by the browser vendor and might varies depending on your operating system and user language setting.
</details>
### Customize messages

Although we haven't define any error messages yet, the form above should be able to populate some message depends on the conditions. These messages are provided by the browser vendor and might vary depending on your users operating system and language setting. Let's customize messages based on the elements' [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState).

```tsx
import { useForm, parse, getFormElements } from '@conform-to/react';

### Styling input
export default function LoginForm() {
const form = useForm({
onValidate({ form, formData }) {
/**
* By parsing the formData, you will be able to access:
* 1) The value in the defined structure
* e.g. { email: '', password: '' }
* 2) The error found while parsing the form data
* e.g. [ ['email', 'Email is required'], ... ]
* 3) The type and intent of the submission
*
* More details will be covered in the submission section
*/
const submission = parse(formData);

/**
* The `getFormElements` returns all input/select/textarea/button
* elements in the form
*/
for (const element of getFormElements(form)) {
switch (element.name) {
case 'email': {
if (element.validity.valueMissing) {
/**
* This will be true when the input is marked as `required`
* while the input is blank
*/
submission.error.push([element.name, 'Email is required']);
} else if (element.validity.typeMismatch) {
/**
* This will be true when the input type is `email`
* while the value does not match
*/
submission.error.push([element.name, 'Email is invalid']);
} else if (!element.value.endsWith('gmail.com')) {
/**
* You can also validate the field manually with custom logic
*/
submission.error.push([element.name, 'Only gmail is accepted']);
}
break;
}
case 'password': {
if (element.validity.valueMissing) {
submission.error.push([element.name, 'Password is required']);
}
break;
}
}
}

return submission;
},

It might be common to update the class name based on the error state. However, conform makes it possible to style using a combination of [CSS pseudo-class](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#the_constraint_validation_api) with data attribute as well:
// ....
});

```css
input[data-conform-touched]:invalid {
border-color: red;
// ...
}
```

### Early reporting

Currently, form error will not be reported until a submission is made. If you want it to be shown earlier, just set the `initialReport` option to `onBlur` and now error should be reported once the user leave the field:
Currently, form error is reported only when a submission is made. If you want it to be shown earlier, you can set the `initialReport` option to `onBlur` and then error will be reported once the user leave the field.

```tsx
import { useForm } from '@conform-to/react';

export default function LoginForm() {
const form = useForm({
/**
* Define when the error should be reported initially.
* Support "onSubmit", "onChange", "onBlur".
*
* Default to `onSubmit`.
*/
initialReport: 'onBlur',
onSubmit(event, { formData }) {
// ...
},
});

return (
<form {...form.props}>
{/* ... */}
<button type="submit">Login</button>
</form>
);
// ...
}
```

Expand Down
Loading

0 comments on commit 91e4c3e

Please sign in to comment.