Skip to content
AzuAzu

Component props

Your components get typed props. Import the types from @mezielabs/azu-react. They’re defined in @mezielabs/azu, so a future Vue package can share them.

For a field’s component: field.text('sku').component('SkuInput').

type FieldProps<Value = unknown> = {
value: Value | null
onChange: (value: Value | null) => void
errors: string[]
field: {
name: string
label: string
/** Required state inferred from the validator */
required?: boolean
}
/** The form's other values */
record?: Record<string, unknown>
}

The demo’s SkuInput renders its own label and errors:

inertia/admin/components/sku_input.tsx
// inertia/admin/components/sku_input.tsx
import { useId } from 'react'
import type { FieldProps } from '@mezielabs/azu-react'
export default function SkuInput({ field, value, onChange, errors }: FieldProps<string>) {
const id = useId()
const errorId = `${id}-error`
return (
<div className="azu-field">
<label className="azu-label" htmlFor={id}>
{field.label}
{field.required && (
<span className="azu-required" aria-hidden>
{' '}
*
</span>
)}
</label>
<input
id={id}
name={field.name}
className="azu-input"
value={value ?? ''}
onChange={(e) => onChange(e.target.value.toUpperCase())}
required={field.required}
aria-invalid={errors.length > 0}
aria-describedby={errors.length > 0 ? errorId : undefined}
/>
{errors.length > 0 && (
<span id={errorId} className="azu-field-error">
{errors.join(' ')}
</span>
)}
</div>
)
}

For a column’s component: column.custom('stock', 'StockBar'), or .component('Name') on any column.

type CellProps<Value = unknown> = {
/** The cell's value, as the column kind serialises it */
value: Value | null
/** The row: its id, title, every cell's value, and what the user may do with it */
record: ListRow
column: ColumnSchema
}
inertia/admin/components/stock_bar.tsx
// inertia/admin/components/stock_bar.tsx
import type { CellProps } from '@mezielabs/azu-react'
export default function StockBar({ value }: CellProps<number>) {
const stock = value ?? 0
return (
<span style={{ color: stock < 10 ? 'var(--azu-danger)' : undefined }}>{stock} in stock</span>
)
}

For a component in one of a resource’s slots.

type SlotProps = {
resource: ResourceSchema
page: 'index' | 'create' | 'edit' | 'show'
/** The record being edited or shown; null on the list and create pages */
record: { id: string | number; title: string | null } | null
/** The resource's list URL, e.g. /admin/resources/products */
path: string
}
inertia/admin/components/print_order.tsx
// inertia/admin/components/print_order.tsx
import type { SlotProps } from '@mezielabs/azu-react'
export default function PrintOrder({ page }: SlotProps) {
// The toolbar slot is on every Orders page; printing is for one order
if (page !== 'show') return null
return (
<button type="button" className="azu-button" onClick={() => window.print()}>
Print
</button>
)
}

For a screen’s component. Pass the screen’s type, and data is typed from its data function.

type ScreenProps<S extends Screen<any> = Screen<unknown>> = {
data: S['__data']
screen: ScreenSchema
}

screen is the screen’s schema: its name, label, and the rest of what the navigation shows. The demo’s SalesReport component is on the Screens page.