Presets

Blocks

Get PRO

Need premium blocks and templates? Upgrade to PRO and get access.

Upgrade
JoinLogin
Presets
Monochrome
Overview
  • Introduction
  • Components
  • Installation
Components
  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Button Group
  • CalendarNEW
  • Card
  • CarouselNEW
  • Checkbox
  • Collapsible
  • Dialog
  • Dropdown Menu
  • Empty
  • Field
  • Input
  • Input Group
  • Item
  • Kbd
  • Label
  • Native Select
  • Scroll Area
  • SelectNEW
  • Separator
  • SwitchNEW
  • Table
  • Tabs
  • Textarea
  • TooltipNEW

Field

PreviousNext

Combine labels, controls, and help text to compose accessible form fields and grouped inputs.

Installation

pnpm dlx sprawlify@latest add field
npx sprawlify@latest add field
yarn sprawlify@latest add field
bunx --bun sprawlify@latest add field

Install the following dependencies:

pnpm add @sprawlify/primitives @sprawlify/svelte
npm install @sprawlify/primitives @sprawlify/svelte
yarn add @sprawlify/primitives @sprawlify/svelte
bun add @sprawlify/primitives @sprawlify/svelte

Add the following files to your project:

field/field-content.svelte
1<script lang="ts">
2import { cn } from "@/lib/utils"
3import { Sprawlify, type PolymorphicProps } from "@sprawlify/svelte"
4import type { HTMLAttributes } from "svelte/elements"
5
6interface Props extends HTMLAttributes<HTMLDivElement>, PolymorphicProps<"div"> {}
7
8let { class: className, children, ...rest }: Props = $props()
9</script>
10
11<Sprawlify
12  as="div"
13  data-scope="field"
14  data-part="content"  
15  data-slot="field-content"
16  class={cn(
17    "group/field-content flex flex-1 flex-col gap-0.5 leading-snug",
18    className
19  )}
20  {...rest}
21>
22  {@render children?.()}
23</Sprawlify>
24
field/field-description.svelte
1<script lang="ts">
2import { cn } from "@/lib/utils"
3import { Field as FieldPrimitive } from "@sprawlify/svelte/field"
4import type { ComponentProps } from "svelte"
5
6interface Props extends ComponentProps<typeof FieldPrimitive.HelperText> {}
7
8let { class: className, children, ...rest }: Props = $props()
9</script>
10
11<FieldPrimitive.HelperText
12  data-slot="field-description"
13  class={cn(
14    "text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
15    "last:mt-0 nth-last-2:-mt-1",
16    "[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
17    className
18  )}
19  {...rest}
20>
21  {@render children?.()}
22</FieldPrimitive.HelperText>
23
field/field-error.svelte
1<script lang="ts">
2import { cn } from "@/lib/utils"
3import { Field as FieldPrimitive } from "@sprawlify/svelte/field"
4import { Sprawlify } from "@sprawlify/svelte"
5import type { ComponentProps } from "svelte"
6
7interface Props extends ComponentProps<typeof FieldPrimitive.ErrorText> {
8  errors?: Array<{ message?: string } | undefined>
9}
10
11let { class: className, children, errors, ...rest }: Props = $props()
12
13const content = $derived.by(() => {
14  if (children) return children
15
16  if (!errors?.length) return null
17
18  const uniqueErrors = [
19    ...new Map(errors.map((error) => [error?.message, error])).values(),
20  ]
21
22  return uniqueErrors
23})
24</script>
25
26{#if content}
27  <FieldPrimitive.ErrorText
28    data-slot="field-error"
29    class={cn("text-sm font-normal text-destructive", className)}
30    {...rest}
31  >
32    {#if children}
33      {@render children()}
34    {:else if Array.isArray(content)}
35      {#if content.length === 1}
36        {content[0]?.message}
37      {:else}
38        <Sprawlify as="ul" class="ml-4 flex list-disc flex-col gap-1">
39          {#each content as error, i (i)}
40            {#if error?.message}
41              <Sprawlify as="li">{error.message}</Sprawlify>
42            {/if}
43          {/each}
44        </Sprawlify>
45      {/if}
46    {/if}
47  </FieldPrimitive.ErrorText>
48{/if}
49
field/field-group.svelte
1<script lang="ts">
2import { cn } from "@/lib/utils"
3import { Sprawlify, type PolymorphicProps } from "@sprawlify/svelte"
4import type { HTMLAttributes } from "svelte/elements"
5
6interface Props extends HTMLAttributes<HTMLDivElement>, PolymorphicProps<"div"> {}
7
8let { class: className, children, ...rest }: Props = $props()
9</script>
10
11<Sprawlify
12  as="div"
13  data-scope="field"
14  data-part="group"
15  data-slot="field-group"
16  class={cn(
17    "group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
18    className
19  )}
20  {...rest}
21>
22  {@render children?.()}
23</Sprawlify>
24
field/field-label.svelte
1<script lang="ts">
2import { cn } from "@/lib/utils"
3import { Field as FieldPrimitive } from "@sprawlify/svelte/field"
4import type { ComponentProps } from "svelte"
5
6interface Props extends ComponentProps<typeof FieldPrimitive.Label> {}
7
8let { class: className, children, ...rest }: Props = $props()
9</script>
10
11<FieldPrimitive.Label
12  data-slot="field-label"
13  class={cn(
14    "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-[state=checked]:border-primary/30 has-data-[state=checked]:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5 dark:has-data-[state=checked]:border-primary/20 dark:has-data-[state=checked]:bg-primary/10",
15    "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
16    className
17  )}
18  {...rest}
19>
20  {@render children?.()}
21</FieldPrimitive.Label>
22
field/field-legend.svelte
1<script lang="ts">
2import { cn } from "@/lib/utils"
3import { Fieldset as FieldsetPrimitive } from "@sprawlify/svelte/fieldset"
4import type { ComponentProps } from "svelte"
5
6interface Props extends ComponentProps<typeof FieldsetPrimitive.Legend> {
7  variant?: "legend" | "label"
8}
9
10let { class: className, variant = "legend", children, ...rest }: Props = $props()
11</script>
12
13<FieldsetPrimitive.Legend
14  data-slot="field-legend"
15  data-variant={variant}
16  class={cn(
17    "mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
18    className
19  )}
20  {...rest}
21>
22  {@render children?.()}
23</FieldsetPrimitive.Legend>
24
field/field-required-indicator.svelte
1<script lang="ts">
2import { cn } from "@/lib/utils"
3import { Field as FieldPrimitive } from "@sprawlify/svelte/field"
4import type { ComponentProps } from "svelte"
5
6interface Props extends ComponentProps<typeof FieldPrimitive.RequiredIndicator> {}
7
8let { class: className, children, ...rest }: Props = $props()
9</script>
10
11<FieldPrimitive.RequiredIndicator
12  data-slot="field-required-indicator"
13  class={cn("text-destructive", className)}
14  {...rest}
15>
16  {@render children?.()}
17</FieldPrimitive.RequiredIndicator>
18
field/field-separator.svelte
1<script lang="ts">
2import { cn } from "@/lib/utils"
3import { Separator } from "@/components/ui/separator"
4import { Sprawlify, type PolymorphicProps } from "@sprawlify/svelte"
5import type { Snippet } from "svelte"
6import type { HTMLAttributes } from "svelte/elements";
7
8interface Props extends HTMLAttributes<HTMLDivElement>, PolymorphicProps<"div"> {
9  children?: Snippet
10}
11
12let { class: className, children, ...rest }: Props = $props()
13</script>
14
15<Sprawlify
16  as="div"
17  data-scope="field"
18  data-part="separator"
19  data-slot="field-separator"
20  data-content={!!children}
21  class={cn(
22    "relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
23    className
24  )}
25  {...rest}
26>
27  <Separator class="absolute inset-0 top-1/2" />
28  {#if children}
29    <Sprawlify
30      as="span"
31      class="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
32      data-scope="field"
33      data-part="separator-content"
34      data-slot="field-separator-content"
35    >
36      {@render children()}
37    </Sprawlify>
38  {/if}
39</Sprawlify>
40
field/field-set.svelte
1<script lang="ts">
2import { cn } from "@/lib/utils"
3import { Fieldset as FieldsetPrimitive } from "@sprawlify/svelte/fieldset"
4import type { ComponentProps } from "svelte"
5
6interface Props extends ComponentProps<typeof FieldsetPrimitive.Root> {}
7
8let { class: className, children, ...rest }: Props = $props()
9</script>
10
11<FieldsetPrimitive.Root
12  data-slot="field-set"
13  class={cn(
14    "flex flex-col gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
15    className
16  )}
17  {...rest}
18>
19  {@render children?.()}
20</FieldsetPrimitive.Root>
21
field/field-title.svelte
1<script lang="ts">
2import { cn } from "@/lib/utils"
3import { Sprawlify, type PolymorphicProps } from "@sprawlify/svelte"
4import type { HTMLAttributes } from "svelte/elements"
5
6interface Props extends HTMLAttributes<HTMLDivElement>, PolymorphicProps<"div"> {}
7
8let { class: className, children, ...rest }: Props = $props()
9</script>
10
11<Sprawlify
12  as="div"
13  data-scope="field"
14  data-part="title"
15  data-slot="field-title"
16  class={cn(
17    "flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50",
18    className
19  )}
20  {...rest}
21>
22  {@render children?.()}
23</Sprawlify>
24
field/field.svelte
1<script lang="ts">
2import { cn } from "@/lib/utils"
3import { Field as FieldPrimitive } from "@sprawlify/svelte/field"
4import type { ComponentProps } from "svelte"
5
6type Orientation = "vertical" | "horizontal" | "responsive"
7
8interface Props extends ComponentProps<typeof FieldPrimitive.Root> {
9  orientation?: Orientation
10}
11
12let { class: className, orientation = "vertical", children, ...rest }: Props = $props()
13
14const orientationClasses: Record<Orientation, string> = {
15  vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
16  horizontal:
17    "has-[>[data-slot=field-content]]:&>[role=checkbox],[role=radio]]:mt-px flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto",
18  responsive:
19    "@md/field-group:has-[>[data-slot=field-content]]:&>[role=checkbox],[role=radio]]:mt-px flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto",
20}
21</script>
22
23<FieldPrimitive.Root
24  data-slot="field"
25  data-orientation={orientation}
26  class={cn(
27    "group/field flex w-full gap-2 data-[invalid=true]:text-destructive",
28    orientationClasses[orientation],
29    className
30  )}
31  {...rest}
32>
33  {@render children?.()}
34</FieldPrimitive.Root>
35
field/index.ts
1export { default as Field } from "./field.svelte";2export { default as FieldLabel } from "./field-label.svelte";3export { default as FieldDescription } from "./field-description.svelte";4export { default as FieldError } from "./field-error.svelte";5export { default as FieldGroup } from "./field-group.svelte";6export { default as FieldLegend } from "./field-legend.svelte";7export { default as FieldSeparator } from "./field-separator.svelte";8export { default as FieldSet } from "./field-set.svelte";9export { default as FieldContent } from "./field-content.svelte";10export { default as FieldTitle } from "./field-title.svelte";11export { default as FieldRequiredIndicator } from "./field-required-indicator.svelte";12

Update the import paths to match your project setup.

Usage

1import {
2  FieldSet,
3  FieldLegend,
4  FieldGroup,
5  Field,
6  FieldContent,
7  FieldLabel,
8  FieldTitle,
9  FieldDescription,
10  FieldSeparator,
11  FieldError
12} from "@/components/ui/field"
1<FieldSet>
2  <FieldLegend>Profile</FieldLegend>
3  <FieldDescription>This appears on invoices and emails.</FieldDescription>
4  <FieldGroup>
5    <Field>
6      <FieldLabel for="name">Full name</FieldLabel>
7      <Input id="name" autoComplete="off" placeholder="Evil Rabbit" />
8      <FieldDescription>This appears on invoices and emails.</FieldDescription>
9    </Field>
10    <Field>
11      <FieldLabel for="username">Username</FieldLabel>
12      <Input id="username" autoComplete="off" aria-invalid />
13      <FieldError>Choose another username.</FieldError>
14    </Field>
15    <Field orientation="horizontal">
16      <Switch id="newsletter" />
17      <FieldLabel for="newsletter">Subscribe to the newsletter</FieldLabel>
18    </Field>
19  </FieldGroup>
20</FieldSet>

Examples

Checkbox

1<script module lang="ts">
2  import { Checkbox } from "@/components/ui/checkbox";
3  import {
4    Field,

Field Group

1<script module lang="ts">
2  import { Checkbox } from "@/components/ui/checkbox"
3  import {
4    Field,

Fieldset

1<script module lang="ts">
2  import {
3    Field,
4    FieldDescription,

Input

1<script module lang="ts">
2  import {
3    Field,
4    FieldDescription,

Textarea

1<script module lang="ts">
2  import {
3    Field,
4    FieldDescription,

On This Page

InstallationUsageExamplesCheckboxField GroupFieldsetInputTextarea

Get PRO

Need premium blocks and templates? Upgrade to PRO and get access.