@arqel-dev/hooks — API Reference
10 reusable hooks. 11 tree-shakeable subpath entry points.
import { useResource, useArqelForm, useCanAccess, useFlash, useTable,
useAction, useFieldDependencies, useNavigation, useBreakpoint,
useArqelOptimistic } from '@arqel-dev/hooks';useResource<T>()
Reads page props + ResourceContext, returns a unified shape.
const { resource, record, fields, form, table } = useResource<Post>();useArqelForm({ fields, record? })
Wrapper of Inertia useForm with defaults via buildInitialFormState.
const form = useArqelForm({ fields, record });
form.data.title // typed via FormValues
form.setData(name, v)
form.processing
form.errors
form.submit('post', route('arqel.resources.store'), { onSuccess: () => {} });Technical note: Inertia's
useForm<T>suffers from "type instantiation excessively deep" withRecord<string, FormDataConvertible>—useArqelFormdoes a bridge castuseForm as unknown as (data: FormValues) => InertiaFormProps<FormValues>to avoid TS2589 while preserving the public return type.
useCanAccess(ability, record?)
const canEdit = useCanAccess('update', post);
const canExport = useCanAccess('exportData'); // global abilityRecord-level abilities take precedence over globals (UX-only — ADR-017).
useFlash({ onMessage })
Once-per-new-message callback via useRef.
useFlash({
onMessage: ({ kind, text }) => toast(text, { type: kind })
});useTable()
Local sort/filters/selection (URL sync deferred to HOOKS-004 follow-up).
const { sort, setSort, filters, setFilter, selection, toggleSelection, clearSelection } = useTable();useAction(action)
router.visit wrapper with a processing flag.
const { invoke, processing } = useAction(action);
<button onClick={() => invoke({ record })} disabled={processing}>...useFieldDependencies()
Debounced 300ms partial Inertia reload of fields.<name>.options.
useFieldDependencies(); // injected in <FieldRenderer>Triggers when a field marked dependsOn(['country']) re-resolves options server-side via Resource.handleDependencyUpdate.
useNavigation()
Reads the Inertia shared prop panel.navigation (populated server-side by HandleArqelInertiaRequests::buildNavigation()).
const { items } = useNavigation();
// items: NavigationItemPayload[]NavigationItemPayload:
type NavigationItemPayload = {
label: string;
url: string;
icon: string | null; // lucide-react ID
group: string | null; // grouping label, null = top-level
sort: number;
active: boolean; // resolved server-side via current request
};Consumed by <Sidebar> (shadcn sidebar-07 block) — items are grouped by group on render.
useTheme()
Re-exported from @arqel-dev/theme. Requires <ThemeProvider> in the tree (included automatically in <ArqelProvider> / createArqelApp). Without the provider, throws a descriptive error.
const { theme, resolved, setTheme, toggle } = useTheme();useBreakpoint()
Tailwind v4 breakpoint via matchMedia. SSR-safe.
const bp = useBreakpoint();
// { current: 'sm' | 'md' | 'lg' | 'xl' | '2xl', isMobile: boolean }useArqelOptimistic()
React 19 useOptimistic wrapper.
const [optimisticPosts, addOptimistic] = useArqelOptimistic(posts, (state, newPost) => [...state, newPost]);Related
- SKILL:
packages-js/hooks/SKILL.md - Next:
@arqel-dev/ui