PIN Input
Allows users to input a sequence of one-character alphanumeric inputs.
	<script lang="ts">
  import { PinInput, Toggle, type PinInputRootSnippetProps } from "bits-ui";
  import { cn } from "$lib/utils/styles.js";
  import { toast } from "svelte-sonner";
  let value = $state("");
 
  type CellProps = PinInputRootSnippetProps["cells"][0];
 
  function onComplete() {
    toast.success("Completed with value " + value);
    value = "";
  }
</script>
 
<PinInput.Root
  bind:value
  class="group/pininput flex items-center text-foreground has-[:disabled]:opacity-30"
  maxlength={6}
  {onComplete}
>
  {#snippet children({ cells })}
    <div class="flex">
      {#each cells.slice(0, 3) as cell}
        {@render Cell(cell)}
      {/each}
    </div>
 
    <div class="flex w-10 items-center justify-center">
      <div class="h-1 w-3 rounded-full bg-border"></div>
    </div>
 
    <div class="flex">
      {#each cells.slice(3, 6) as cell}
        {@render Cell(cell)}
      {/each}
    </div>
  {/snippet}
</PinInput.Root>
 
{#snippet Cell(props: CellProps)}
  <div
    class={cn(
      // Custom class to override global focus styles
      "focus-override",
      "relative h-14 w-10 text-[2rem]",
      "flex items-center justify-center",
      "transition-all duration-200",
      "border-y border-r border-foreground/20 first:rounded-l-md first:border-l last:rounded-r-md",
      "text-foreground group-focus-within/pininput:border-foreground/40 group-hover/pininput:border-foreground/40",
      "outline outline-0",
      props.isActive && "outline-1 outline-white"
    )}
  >
    {#if props.char !== null}
      <div>
        {props.char}
      </div>
    {/if}
    {#if props.hasFakeCaret}
      <div
        class="pointer-events-none absolute inset-0 flex animate-caret-blink items-center justify-center"
      >
        <div class="h-8 w-px bg-white"></div>
      </div>
    {/if}
  </div>
{/snippet}
	
This component is derived from and would not have been possible without the work done by Input OTP by Guilherme Rodz.
Structure
	<script lang="ts">
	import { PinInput } from "bits-ui";
</script>
 
<PinInput.Root maxlength={6}>
	{#snippet children({ cells })}
		{#each cells as cell}
			{cell.char !== null ? cell.char : ""}
		{/each}
	{/snippet}
</PinInput.Root>
	
API Reference
The root component which contains the pin-input.
| Property | Type | Description | 
|---|---|---|
| placeholder | string | The placeholder character to use for empty pin-inputs. Default:  ○ | 
| value | string[] | The value of the pin-input. Default:  undefined | 
| name | string | The name of the pin-input. This is used for form submission. Default:  undefined | 
| disabled | boolean | Whether or not the pin-input is disabled. Default:  false | 
| type | 'text' | 'password' | The type of the input. Use  Default:  text | 
| onValueChange | function | A callback function called when the pin-input value changes. Default:  undefined | 
| asChild | boolean | Whether to use render delegation with this component or not. Default:  false | 
| el | HTMLDivElement | The underlying DOM element being rendered. You can bind to this to programatically interact with the element. Default:  undefined | 
| Slot Property | Type | Description | 
|---|---|---|
| builder | object | The builder attributes and actions to apply to the element if using the  | 
| ids | object | The ids of the elements within the component, useful when you don't necessarily want to provide a custom ID, but still want access to the ID being assigned (if any). | 
| Data Attribute | Value | Description | 
|---|---|---|
| data-pin-input-root | —— | Present on the root element. | 
| data-complete | —— | Present if the pin-input is complete. | 
The input component which contains the pin-input.
| Property | Type | Description | 
|---|---|---|
| asChild | boolean | Whether to use render delegation with this component or not. Default:  false | 
| el | HTMLInputElement | The underlying DOM element being rendered. You can bind to this to programatically interact with the element. Default:  undefined | 
| Data Attribute | Value | Description | 
|---|---|---|
| data-pin-input-input | —— | Present on the input element. | 
| data-complete | —— | Present if the pin-input is complete. | 
The hidden input component which contains the pin-input.
| Property | Type | Description | 
|---|---|---|
| asChild | boolean | Whether to use render delegation with this component or not. Default:  false | 
| el | HTMLInputElement | The underlying DOM element being rendered. You can bind to this to programatically interact with the element. Default:  undefined | 
| Data Attribute | Value | Description | 
|---|---|---|
| data-pin-input-hidden-input | —— | Present on the hidden input element. |