Skip to content
WANCRAFT

F.175Forms & Inputs

Slug Input

A URL slug field that follows the title until edited, previews the full URL and checks availability as you type.

NewReactMotionTailwindintermediate

Overview

Slug Input keeps a post's URL in step with its title. While it is in auto mode the slug is derived from the source text: accents are stripped with Unicode NFKD normalisation ("Crème brûlée" becomes "creme-brulee"), ligatures such as ß and æ are spelled out, everything is lowercased and joined with single hyphens, and long titles are trimmed at a word boundary. Typing in the field switches to manual mode and reveals a "Reset to auto" link. Manual slugs are validated for allowed characters with an error that names the offending ones, the full URL is previewed with the prefix and can be copied, and an optional async availability check runs after a debounce, aborts stale requests and reports checking, available or taken with a one-click suggestion.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/slug-input.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/slug-input.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/slug-input.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { SlugInput } from "@/components/ui/slug-input";

export function SlugInputExample() {
  const title = "Crème brûlée & other Unicode slugs";
  const taken = ["about", "pricing", "hello-world"];

  return (
    <div className="w-full max-w-md">
      <SlugInput
        source={title}
        checkAvailability={async (slug) => (taken.includes(slug) ? { available: false, suggestion: `${slug}-2` } : true)}
        onChange={(slug, auto) => console.log(slug, auto)}
      />
    </div>
  );
}

03Props

PropTypeDefaultDescription
sourcestring""Text the slug follows (usually the title) until the user edits the slug.
defaultValuestring—Start in manual mode with this slug.
onChange(slug: string, auto: boolean) => void—Called whenever the effective slug changes, including auto updates from source.
checkAvailability(slug, signal: AbortSignal) => Promise<boolean | { available; suggestion? }>—Async check, debounced; the signal aborts when the slug changes again.
onStatusChange(status: SlugStatus) => void—idle, checking, available, taken or error — e.g. to disable a submit button.
prefixstring"https://wancraft.my/blog/"URL the slug is appended to; shown (without protocol) as an addon and in full in the preview.
maxLengthnumber60Maximum length; auto slugs are trimmed at a word boundary.
debouncenumber400Milliseconds to wait after typing stops before checking.
showPreviewbooleantrueShow the full URL preview line.
showCopybooleantrueShow the Copy URL button (Clipboard API).
label / hintReactNode"Slug" / —Visible label and helper text (aria-describedby).
accentstring"#FF7448"Focus ring, caret, slug underline and copy check.
namestring"slug"Name of the submitted text input.
disabled / inputIdboolean / string—Disable the field; set the input id.

04Features

  • 01Auto-generates from the title with NFKD accent stripping, ß/æ/ø spellings, & → and, single hyphens and a word-boundary length cap
  • 02Switches to manual on edit, with a "Reset to auto" link that returns to following the title
  • 03Validation that names the disallowed characters and catches leading, trailing or repeated hyphens
  • 04Debounced, abortable availability check with checking, available, taken and error states
  • 05Taken slugs offer a one-click suggestion; full URL preview with a copy button
  • 06The exported slugify() helper can be reused on the server for the same result
motion · tweeninput · keyboardinput · focusinput · click

05Accessibility

  • A real <label> names the input; hint or error, the character count and the availability status are joined in aria-describedby.
  • Errors set aria-invalid and appear in a polite live region; a taken slug also sets aria-invalid.
  • Availability results, copy, and reset are announced in a polite live region ("creme-brulee is available").
  • The status icon is decorative; every state is also written out as text.
  • Reset to auto, the suggestion and Copy URL are real buttons with visible focus rings.
  • prefers-reduced-motion stops the spinner and the message slide.

06Limitations

  • Transliteration covers Latin scripts; non-Latin titles (e.g. Cyrillic, CJK) produce an empty auto slug and need a manual one or your own slugify.