iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
📝

Implementation Guide for Checkbox with shadcn/ui and conform

に公開

Please also see other articles in this series:

By combining these articles, you can gain comprehensive knowledge of form implementation using shadcn/ui and conform.

Overview

In this article, we will explain how to implement a basic Checkbox using shadcn/ui and conform. We will introduce both the standard implementation method and a method using custom helper functions.

For the specifications and usage of the shadcn/ui Checkbox component itself, please refer to the official website.

https://ui.shadcn.com/docs/components/checkbox

Setup

First, import the necessary libraries.

import { useForm } from '@conform-to/react'
import { getZodConstraint, parseWithZod } from '@conform-to/zod'
import { z } from 'zod'
import { Checkbox } from '~/components/ui/checkbox'
import { Label } from '~/components/ui/label'
import { getCheckboxProps } from './helper'

The basic form structure is as follows:

export default function CheckboxForm() {
  const [form, fields] = useForm({
    onValidate: ({ formData }) => parseWithZod(formData, { schema }),
    constraint: getZodConstraint(schema),
    shouldValidate: 'onBlur',
    shouldRevalidate: 'onInput',
  })

  return (
    <form {...getFormProps(form)} method="post">
      {/* Checkbox fields will be placed here */}
    </form>
  )
}

Schema Definition

const schema = z.object({
  agreeTerms: z.boolean({
    required_error: 'Please agree to the terms of service',
    message: 'Invalid selection',
  })
})

Implementation Examples

1. Basic Checkbox Implementation

<div className="flex items-center space-x-2">
  <Checkbox
    key={fields.agreeTerms.key}
    id={fields.agreeTerms.id}
    name={fields.agreeTerms.name}
    required={fields.agreeTerms.required}
    defaultChecked={fields.agreeTerms.initialValue === 'on'}
    aria-invalid={!fields.agreeTerms.valid || undefined}
    aria-describedby={!fields.agreeTerms.valid ? fields.agreeTerms.errorId : undefined}
    onCheckedChange={(checked) => {
      form.update({
        name: fields.agreeTerms.name,
        value: checked,
      })
    }}
  />
  <Label
    htmlFor={fields.agreeTerms.id}
    className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
  >
    Agree to the terms of service
  </Label>
</div>
<div id={fields.agreeTerms.errorId} className="text-destructive">
  {fields.agreeTerms.errors}
</div>

2. Checkbox Implementation using Helper Functions

You can implement it more concisely using the getCheckboxProps function defined in the helper.ts file.

<div className="flex items-center space-x-2">
  <Checkbox
    {...getCheckboxProps(fields.agreeTerms)}
    key={fields.agreeTerms.key}
    onCheckedChange={(checked) => {
      form.update({
        name: fields.agreeTerms.name,
        value: checked,
      })
    }}
  />
  <Label
    htmlFor={fields.agreeTerms.id}
    className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
  >
    Agree to the terms of service
  </Label>
</div>
<div id={fields.agreeTerms.errorId} className="text-destructive">
  {fields.agreeTerms.errors}
</div>

Explanation of the Helper Function

In the helper.ts file, the getCheckboxProps function is defined to simplify the implementation of the Checkbox:

helper.ts
import type { FieldMetadata } from '@conform-to/react';

/**
 * Cleanup `undefined` from the result.
 * To minimize conflicts when merging with user defined props
 */
function simplify<Props>(props: Props): Props {
  for (const key in props) {
    if (props[key] === undefined) {
      delete props[key];
    }
  }
  return props;
}

export const getCheckboxProps = <Schema>(
  metadata: FieldMetadata<Schema>,
  options:
    | {
        ariaAttributes?: true;
        ariaInvalid?: 'errors' | 'allErrors';
        ariaDescribedBy?: string;
        value?: boolean;
      }
    | {
        ariaAttributes: false;
        value?: boolean;
      } = {
    ariaAttributes: true,
  }
) => {
  const props: {
    id: string;
    key?: string;
    required?: boolean;
    name: string;
    defaultChecked?: boolean;
    'aria-invalid'?: boolean;
    'aria-describedby'?: string;
  } = {
    id: metadata.id,
    key: metadata.key,
    required: metadata.required,
    name: metadata.name,
  };

  if (typeof options.value === 'undefined' || options.value) {
    props.defaultChecked = metadata.initialValue === 'on';
  }

  if (options.ariaAttributes) {
    const invalid =
      options.ariaInvalid === 'allErrors'
        ? !metadata.valid
        : typeof metadata.errors !== 'undefined';
    const ariaDescribedBy = options.ariaDescribedBy;
    props['aria-invalid'] = invalid || undefined;
    props['aria-describedby'] = invalid
      ? `${metadata.errorId} ${ariaDescribedBy ?? ''}`.trim()
      : ariaDescribedBy;
  }

  return simplify(props);
};

This function generates the following properties:

  • id: Checkbox ID
  • key: React key attribute
  • required: Whether it is a required field
  • name: Field name
  • defaultChecked: Initial checked state
  • aria-invalid: Whether the input is invalid
  • aria-describedby: Associates the error message

By using these properties, you can maintain a consistent implementation while considering accessibility.

Debugging and Testing

You can add a debug section to check the form values and errors:

<div>
  <h3>Form values</h3>
  <pre>{JSON.stringify(form.value, null, 2)}</pre>
</div>
<div>
  <h3>Form errors</h3>
  <pre>{JSON.stringify(form.allErrors, null, 2)}</pre>
</div>

This allows you to easily verify the state of the form.

Demo and Implementation Examples

If you want to see the actual behavior of what was explained in this article, please check the following demo page:

Form implementation demo using shadcn/ui and conform

On this demo page, you can see implementation examples for various form elements combining shadcn/ui and conform. You can check the behavior of each element, which is useful for understanding the actual user experience.

Also, you can access the source code for the implementation from the demo page. By referring to the source code, you can see in detail how the implementation methods explained in this article are applied.

By checking both the demo and the source code, you can deepen your understanding from both theoretical and practical perspectives. Please visit the demo page to check the actual behavior and code details.

Summary

By combining shadcn/ui and conform, you can easily implement a basic Checkbox. Furthermore, by using custom helper functions, you can make your code more concise and improve maintainability. By performing appropriate validation and error handling, you can create user-friendly checkbox forms.

Next time, I will explain how to implement Switch in detail. Stay tuned!

GitHubで編集を提案

Discussion