You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
React version of javaabu/forms, published as @javaabu/forms. The repo has a v0 @javaabu/react-forms built only on Onu; rework it to be UI-framework-agnostic, with Onu and base shadcn/ui adapters, native Inertia and Wayfinder support, model binding, automatic errors and @javaabu/i18n strings.
Built to the next-gen general rules: CI with test and coverage badges, full docs for people and agents, Laravel Boost guideline and skill, Javaabu codestyle, PHP 8.4 / Laravel 12+ (Laravel packages) or TypeScript (JS packages), Dhivehi and RTL, Blade and Inertia React for views, overridable controllers and policies with javaabu/permissions.
All work happens on the next-gen branch; one PR into main at the end.
Architecture
src/
core/ no UI imports: FormsProvider, adapter registry, binding / old input, errors, field registry, Form submit logic, strings
components/ the Blade components (Text, Select2, Checkbox, Table, ...). They resolve names, ids, values and errors, then render adapter slots
adapters/
html/ plain elements. The fallback for any slot an adapter leaves out, and the reference implementation
onu/ @javaabu/forms/onu (+ /onu/editor and /onu/maps for the heavy Onu entries)
shadcn/ @javaabu/forms/shadcn: createShadcnAdapter({ Input, Select, Checkbox, ... })
inertia/ @javaabu/forms/inertia: router integration (visit, page errors and bags, Link). @inertiajs/react is an optional peer
testing/ @javaabu/forms/testing: adapter conformance kit
Adapter = a plain object { name, components: Partial<Slots>, confirm?, toast? }. Resolution order for each slot: <FormsProvider components> override, then the ui adapter, then the HTML base. Adapters can therefore be partial, and you can combine several by spreading them ({ ...onu, ...onuEditor }).
Slots are named after the Blade components where they map one to one (Input, Textarea, Checkbox, Radio, Select, Date, File, Image, Number, Wysiwyg, MapInput, Button, LinkButton, ButtonGroup, Alert, Card*, Accordion, Tabs, NavTabs, Modal, Status, Table*, EmptyState, Pagination). They also cover the scaffolding: Form, Field, Label, RequiredMarker, HelpText, FieldErrors, ErrorSummary, Link, Icon, ActionMenu, DescriptionList.
Core owns the state. Every control is controlled by the core (value + onValueChange, or native props with a composed onChange for native-element slots). Ids, aria-invalid and aria-describedby come from the core. Native FormData stays the source of truth on submit, as in Blade. Adapters only render.
Strings use @javaabu/i18n with Blade's keys (forms::strings.nothing_selected). Lines the app loads (from lang/vendor/forms/{locale}/strings.php) win over the bundled en/dv defaults. Generated labels go through __(), as Blade's trans() does.
Public API sketch
import{FormsProvider,Form,Text,Select2,Submit}from"@javaabu/forms"import{onu}from"@javaabu/forms/onu"import{inertia}from"@javaabu/forms/inertia"import{store,update}from"@/actions/App/Http/Controllers/UserController"// Wayfinder<FormsProviderui={onu}router={inertia}components={{Select: MySelect}}><Formmodel={user}action={update}>{/* Wayfinder: called with the model, PUT spoofed */}<Textname="name"required/><Select2name="country"options={countries}relation/><Submit/></Form></FormsProvider>// Inertia useForm, with no glue code: fields bind to form.data, and errors, processing and progress come from the formconstform=useForm({name: ""})<Formform={form}action={store()}>...</Form>
Submit modes, in order: form (Inertia useForm), then onSubmit, then the provider router (Inertia visit, or the core's fetch router for JSON 422 handling), then native submit. Errors come from Inertia page props (with errorBag), a 422 response, or the errors prop. Keys work in dot or bracket notation, plus items.0.name and tags.*. The ErrorSummary component focuses the first invalid field.
react-hook-form. The form prop of v0 (passed through to Onu's Form) becomes Inertia's useForm. To use react-hook-form, use onSubmit. This is noted in the migration docs.
Wysiwyg and MapInput move into the main entry (their logic is light). The heavy Onu parts move to @javaabu/forms/onu/editor and /onu/maps.
New component.ErrorSummary has no Blade v1 equivalent. It's listed in docs/parity.md so that v2 can add <x-forms::error-summary>.
All next-gen issues are closed except #16, which needs a human. The work is in PR #19 (next-gen → main), left open for you to review and merge.
What was built
@javaabu/forms: the React version of javaabu/forms, with a UI-framework-agnostic core in src/core (no UI imports). Entry points:
. (components, provider, hooks, helpers, the adapter and router contracts, fetchRouter)
/html (the default adapter)
/onu, /onu/editor and /onu/maps
/shadcn (createShadcnAdapter)
/inertia (the router and inertiaLinks)
/testing (the adapter conformance kit, with 21 cases)
Adapters: HTML, Onu and shadcn (tested against stock new-york-v4 components) all pass the conformance kit.
Inertia: visits with files, method spoofing, error bags, progress and preserved state; useForm() binding; no glue code. Wayfinder: route objects, .form() variants and route functions with model binding. Without Inertia:fetchRouter, or a native submit.
Laravel behaviour: binding, old input and errors are checked against Blade.
Strings:@javaabu/i18n with Blade's keys, plus Dhivehi and RTL.
Docs: Docusaurus pages, llms.txt, AGENTS.md, and the Laravel Boost guideline and javaabu-react-forms skill. The docs are kept in sync by src/docs.test.ts.
Examples: a page per integration, verified in headless Chromium. That check found and fixed a real bug: loading submit buttons didn't submit.
CI: lint, format, typecheck, 204 tests with coverage (thresholds at lines 85%, currently about 89%), build and examples, running on next-gen and on PRs.
Intentionally out of scope
No IIFE / <script> build: this is a React library with peer dependencies.
Blade-only attributes (floating, dateFormat, the select2 icon picker, jsonEncode, …) are listed in docs/parity.md.
Image, Pagination, NavTabs and Map have no shadcn implementation. They use the HTML adapter, or you can replace them through components.
The Onu maps adapter is typechecked but not covered by tests, because it needs Google Maps.
What you need to do
Codecov: add CODECOV_TOKEN. Uploads currently fail with "Token required", and the badge stays empty until then.
@javaabu/i18n dependency: it uses github:Javaabu/i18n#build-on-git-install, the branch of Build dist when installed from Git i18n#1, so CI can build it. After that PR merges, switch it to #main. After i18n is on npm, switch it to a version range, ideally as a peer.
Dependents installing from Git with pnpm 10.34 or later must allow the build by its exact tarball URL, or run pnpm approve-builds. This is documented in the installation page.
Activity
n4ff4h commentedon Nov 4, 2026
Implementation plan
All work happens on the
next-genbranch; one PR intomainat the end.Architecture
{ name, components: Partial<Slots>, confirm?, toast? }. Resolution order for each slot:<FormsProvider components>override, then theuiadapter, then the HTML base. Adapters can therefore be partial, and you can combine several by spreading them ({ ...onu, ...onuEditor }).Input,Textarea,Checkbox,Radio,Select,Date,File,Image,Number,Wysiwyg,MapInput,Button,LinkButton,ButtonGroup,Alert,Card*,Accordion,Tabs,NavTabs,Modal,Status,Table*,EmptyState,Pagination). They also cover the scaffolding:Form,Field,Label,RequiredMarker,HelpText,FieldErrors,ErrorSummary,Link,Icon,ActionMenu,DescriptionList.value+onValueChange, or native props with a composedonChangefor native-element slots). Ids,aria-invalidandaria-describedbycome from the core. NativeFormDatastays the source of truth on submit, as in Blade. Adapters only render.@javaabu/i18nwith Blade's keys (forms::strings.nothing_selected). Lines the app loads (fromlang/vendor/forms/{locale}/strings.php) win over the bundleden/dvdefaults. Generated labels go through__(), as Blade'strans()does.Public API sketch
Submit modes, in order:
form(InertiauseForm), thenonSubmit, then the providerrouter(Inertia visit, or the core'sfetchrouter for JSON 422 handling), then native submit. Errors come from Inertia page props (witherrorBag), a 422 response, or theerrorsprop. Keys work in dot or bracket notation, plusitems.0.nameandtags.*. TheErrorSummarycomponent focuses the first invalid field.Order of work
preparescript, coverage and Codecov, examples, badges onnext-gendocs/parity.md(kept up to date throughout), Dhivehi localization and RTL #15 Dhivehi and RTL, Documentation for people and agents #13 docs andllms.txt, Laravel Boost guideline and skill #14 BoostDecisions and open questions
<script>build makes no sense. ESM and CJS only.@javaabu/i18ndependency. It's a regular dependency for now, ongithub:Javaabu/i18n#build-on-git-install(the PR Foundations: TypeScript build, dist, examples, CI and coverage #1 branch, which hasprepare), so CI can install it. I'll switch to#mainonce Build dist when installed from Git i18n#1 is merged, and to a peer dependency once it's on npm.formprop of v0 (passed through to Onu'sForm) becomes Inertia'suseForm. To use react-hook-form, useonSubmit. This is noted in the migration docs.WysiwygandMapInputmove into the main entry (their logic is light). The heavy Onu parts move to@javaabu/forms/onu/editorand/onu/maps.ErrorSummaryhas no Blade v1 equivalent. It's listed indocs/parity.mdso that v2 can add<x-forms::error-summary>.n4ff4h commentedon Nov 4, 2026
Final summary
All
next-genissues are closed except #16, which needs a human. The work is in PR #19 (next-gen→main), left open for you to review and merge.What was built
@javaabu/forms: the React version of javaabu/forms, with a UI-framework-agnostic core insrc/core(no UI imports). Entry points:.(components, provider, hooks, helpers, the adapter and router contracts,fetchRouter)/html(the default adapter)/onu,/onu/editorand/onu/maps/shadcn(createShadcnAdapter)/inertia(the router andinertiaLinks)/testing(the adapter conformance kit, with 21 cases)useForm()binding; no glue code. Wayfinder: route objects,.form()variants and route functions with model binding. Without Inertia:fetchRouter, or a native submit.@javaabu/i18nwith Blade's keys, plus Dhivehi and RTL.docs/parity.md, settled with the javaabu/forms v2 agent in Parity with javaabu/forms v2: defaults, Blade-only attributes and string keys #18.llms.txt, AGENTS.md, and the Laravel Boost guideline andjavaabu-react-formsskill. The docs are kept in sync bysrc/docs.test.ts.next-genand on PRs.Intentionally out of scope
<script>build: this is a React library with peer dependencies.floating,dateFormat, the select2 icon picker,jsonEncode, …) are listed indocs/parity.md.Image,Pagination,NavTabsandMaphave no shadcn implementation. They use the HTML adapter, or you can replace them throughcomponents.What you need to do
CODECOV_TOKEN. Uploads currently fail with "Token required", and the badge stays empty until then.@javaabu/i18ndependency: it usesgithub:Javaabu/i18n#build-on-git-install, the branch of Build dist when installed from Git i18n#1, so CI can build it. After that PR merges, switch it to#main. After i18n is on npm, switch it to a version range, ideally as a peer.next-genintomain. Then release with changesets (pnpm changeset version, then av*tag). Nothing has been tagged or published.docs/advanced-usage/dhivehi-and-rtl.md.next-gen/cross-packagelabels, and I didn't create them.pnpm approve-builds. This is documented in the installation page.