iTranslated by AI
Implementation Guide for Select with shadcn/ui and Conform
Related Articles
Please check out other articles in this series:
- Implementation guide for Input with shadcn/ui and conform
- Implementation guide for Textarea with shadcn/ui and conform
- Implementation guide for Select with shadcn/ui and conform
- Implementation guide for Checkbox with shadcn/ui and conform
- Implementation guide for Switch with shadcn/ui and conform
- Implementation guide for RadioGroup with shadcn/ui and conform
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 Select 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 Select component itself, please refer to the official website.
Setup
First, import the necessary libraries.
import { useForm } from '@conform-to/react'
import { getZodConstraint, parseWithZod } from '@conform-to/zod'
import { z } from 'zod'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '~/components/ui/select'
import { Label } from '~/components/ui/label'
import { getSelectProps, getSelectTriggerProps } from './helper'
The basic form structure is as follows:
export default function SelectForm() {
const [form, fields] = useForm({
onValidate: ({ formData }) => parseWithZod(formData, { schema }),
constraint: getZodConstraint(schema),
shouldValidate: 'onBlur',
shouldRevalidate: 'onInput',
})
return (
<form {...getFormProps(form)} method="post">
{/* Select fields will be placed here */}
</form>
)
}
Schema Definition
const schema = z.object({
basicSelect: z.enum(['apple', 'banana', 'orange'], {
required_error: '選択してください',
message: '無効な選択です',
}),
})
Implementation Examples
1. Basic Select Implementation
<div>
<Label htmlFor={fields.basicSelect.id}>果物を選択</Label>
<Select
key={fields.basicSelect.key}
name={fields.basicSelect.name}
defaultValue={fields.basicSelect.initialValue}
onValueChange={(value) => {
form.update({
name: fields.basicSelect.name,
value,
})
}}
>
<SelectTrigger
id={fields.basicSelect.id}
aria-invalid={!fields.basicSelect.valid || undefined}
aria-describedby={!fields.basicSelect.valid ? fields.basicSelect.errorId : undefined}
>
<SelectValue placeholder="選択してください" />
</SelectTrigger>
<SelectContent>
<SelectItem value="apple">りんご</SelectItem>
<SelectItem value="banana">バナナ</SelectItem>
<SelectItem value="orange">オレンジ</SelectItem>
</SelectContent>
</Select>
<div id={fields.basicSelect.errorId} className="text-destructive">
{fields.basicSelect.errors}
</div>
</div>
2. Select Implementation using Helper Functions
You can implement it more concisely using the getSelectProps and getSelectTriggerProps functions defined in the helper.ts file.
<div>
<Label htmlFor={fields.basicSelect.id}>果物を選択 (Helper使用)</Label>
<Select
{...getSelectProps(fields.basicSelect)}
key={fields.basicSelect.key}
onValueChange={(value) => {
form.update({
name: fields.basicSelect.name,
value,
})
}}
>
<SelectTrigger {...getSelectTriggerProps(fields.basicSelect)}>
<SelectValue placeholder="選択してください" />
</SelectTrigger>
<SelectContent>
<SelectItem value="apple">りんご</SelectItem>
<SelectItem value="banana">バナナ</SelectItem>
<SelectItem value="orange">オレンジ</SelectItem>
</SelectContent>
</Select>
<div id={fields.basicSelect.errorId} className="text-destructive">
{fields.basicSelect.errors}
</div>
</div>
Explanation of Helper Functions
In the helper.ts file, two main functions are defined to simplify the Select implementation:
-
getSelectProps: Generates the properties required for Select. -
getSelectTriggerProps: Generates the properties required for SelectTrigger.
By using these functions, you can reduce code redundancy and maintain a consistent implementation.
The implementation of helper.ts is as follows:
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 getSelectProps = <Schema>(
metadata: FieldMetadata<Schema>,
options: {
value?: boolean;
} = {}
) => {
const props: {
key?: string;
required?: boolean;
name: string;
defaultValue?: string;
} = {
key: metadata.key,
required: metadata.required,
name: metadata.name,
};
if (typeof options.value === 'undefined' || options.value) {
props.defaultValue = metadata.initialValue?.toString();
}
return simplify(props);
};
export const getSelectTriggerProps = <Schema>(
metadata: FieldMetadata<Schema>,
options:
| {
ariaAttributes?: true;
ariaInvalid?: 'errors' | 'allErrors';
ariaDescribedBy?: string;
}
| {
ariaAttributes: false;
} = {
ariaAttributes: true,
}
) => {
const props: {
id: string;
'aria-invalid'?: boolean;
'aria-describedby'?: string;
} = {
id: metadata.id,
};
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);
};
Debugging and Testing
You can add a debugging section to check the form values and errors:
<div>
<h3>フォームの値</h3>
<pre>{JSON.stringify(form.value, null, 2)}</pre>
</div>
<div>
<h3>フォームのエラー</h3>
<pre>{JSON.stringify(form.allErrors, null, 2)}</pre>
</div>
This allows you to easily check the state of the form.
Demo and Implementation Examples
If you would like to see the actual behavior of the content explained in this article, please visit 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 helpful for understanding the actual user experience.
Additionally, you can access the implementation source code from the demo page. By referring to the source code, you can see in detail how the implementation methods described 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 see the actual behavior and code details.
Summary
By combining shadcn/ui and conform, you can easily implement a basic Select. Furthermore, using custom helper functions can make the code more concise and improve maintainability. By performing appropriate validation and error handling, you can create user-friendly selection forms.
In the next article, we will explain Checkbox implementation in detail. Stay tuned!
Discussion