Getting Started
Installation
Install the package
npm install @wire-ui/reactWire UI has no production dependencies. React 19 is a peer dependency.
Add peer dependencies
If you don’t already have React 19:
npm install react@^19 react-dom@^19Use a component
import { Button } from '@wire-ui/react'
export function SaveButton() {
return (
<Button type="submit" className="px-4 py-2 rounded bg-blue-600 text-white">
Save changes
</Button>
)
}Styling approach
Wire UI ships no CSS. All interactive state is exposed through data-* attributes. Target them with any CSS approach you prefer.
<Button className="
px-4 py-2 rounded bg-blue-600 text-white transition-all
[data-hover]:bg-blue-700
[data-active]:scale-95
[data-focus-visible]:ring-2 [data-focus-visible]:ring-offset-2
[data-disabled]:opacity-50 [data-disabled]:cursor-not-allowed
">
Submit
</Button>/* button.module.css */
.button {
padding: 0.5rem 1rem;
border-radius: 0.375rem;
background: #2563eb;
color: white;
}
.button[data-hover] { background: #1d4ed8; }
.button[data-active] { transform: scale(0.95); }
.button[data-focus-visible] { outline: 2px solid #2563eb; }
.button[data-disabled] { opacity: 0.5; cursor: not-allowed; }import styles from './button.module.css'
<Button className={styles.button}>Submit</Button>.button[data-hover] { background: #1d4ed8; }
.button[data-active] { transform: scale(0.95); }
.button[data-focus-visible] { outline: 2px solid #2563eb; }
.button[data-disabled] { opacity: 0.5; cursor: not-allowed; }Compound components
Complex components use the Component.Part pattern. You compose the markup yourself:
import { Input } from '@wire-ui/react'
<Input.Root value={value} onChange={setValue}>
<Input.Label>Email address</Input.Label>
<Input.Field type="email" placeholder="you@example.com" />
<Input.Error />
</Input.Root>You control the DOM order. Need the error above the field? Swap them. Need a wrapper div between label and field? Add it. Wire UI never dictates structure.
Validation pattern
Wire UI components do not validate internally. You own validation — set invalidType when your logic determines the field is invalid:
const [email, setEmail] = useState('')
const [invalidType, setInvalidType] = useState('')
function handleSubmit(e: React.FormEvent) {
e.preventDefault()
if (!email) setInvalidType('required')
else if (!email.includes('@')) setInvalidType('email')
else setInvalidType('') // clear the error
}
<Input.Root
value={email}
onChange={setEmail}
invalidType={invalidType}
errorMessage={{
required: 'Email is required',
email: 'Enter a valid email address',
}}
>
<Input.Label>Email</Input.Label>
<Input.Field type="email" />
<Input.Error />
</Input.Root>TypeScript
All prop types are exported:
import type {
ButtonProps,
InputRootProps,
TextareaRootProps,
PasswordRootProps,
ModalRootProps,
AccordionRootProps,
SearchOption,
Size,
Status,
} from '@wire-ui/react'Last updated on