UI packages
LaraPack generates the same module for Vue and for React from the same laraimport.json. That is why the UI is split the way it is:
- Everything that doesn't depend on a framework lives once: the theme, notifications, requests, routes, languages, validation and masks.
- Components and tables have one twin per framework, with the same names and the same contract.
Two copies of the same code eventually drift; a shared core doesn't. And a generated model behaves the same in both frameworks.
The packages
| Package | Version | Vue / React | What it does | Peer dependencies |
|---|---|---|---|---|
innoboxrr-form-core | 2.8.0 | Both, framework-free | Class theme, --fe-* CSS variables, icons, toasts and confirmations, files, time zones | — |
innoboxrr-form-elements | 6.8.0 | Vue | The 37 components: form controls and desktop pieces | vue ^3.5.0, sortablejs ^1.14.0, lightvue (optional) |
innoboxrr-react-form-elements | 3.8.0 | React | The same 37 components, with the same names | react ^19.0.0, react-dom ^19.0.0 |
innoboxrr-vue-datatable | 3.1.1 | Vue | The admin listing over the model contract | innoboxrr-form-core ^2.6.0, innoboxrr-form-elements ^6.4.0, vue ^3.5.0, vue-router ^4.5.0 |
innoboxrr-react-datatable | 3.1.1 | React | The same listing | innoboxrr-form-core ^2.6.0, innoboxrr-react-form-elements ^3.4.0, react ^19.0.0, react-dom ^19.0.0, react-router-dom ^7.0.0 |
innoboxrr-http-request | 2.0.0 | Both; separate Vue plugin | makeHttpRequest with retries, confirmation and cancellation | vue ^3.5.0 (optional) |
innoboxrr-route-resolver | 2.0.0 | Both | route(name, params) over Laravel's routes.json | — |
innoboxrr-i18n | 1.2.0 | Both | t(), addTranslations, setLocale | — |
innoboxrr-js-validator | 2.0.1 | Both | Validates forms from data-validators | — |
innoboxrr-locale-generator | 2.0.0 | Node CLI | locale-gen: extracts t('…') keys into locales/*.json | — |
innoboxrr-maskjs | 2.0.0 | Both; ./vue and ./react adapters | Input masks | vue ^3.5.0 and react ^18 or ^19, both optional |
- Every package declares
"engines": { "node": ">=20" }. - All are ES modules, except
innoboxrr-locale-generator, which is a CommonJS CLI.
Components ship as source
innoboxrr-form-elements, innoboxrr-react-form-elements and both datatables publish their .vue and .jsx files uncompiled. Your application's Vite compiles them, just like your own components. That is why the styles are imported separately, once.
How they fit
innoboxrr-form-core theme, --fe-* variables, icons, toasts, files
│
innoboxrr-form-elements the 37 components (Vue)
innoboxrr-react-form-elements the same 37 (React)
│
innoboxrr-vue-datatable the listing over the model contract
innoboxrr-react-datatable
│
module generated by LaraPack model contract, forms, views, theme.js, i18n.js
│
base application shell boot: routes, languages, styles, toasts and confirmations1. The theme
innoboxrr-form-core has no dependencies. It keeps, as module state, what must be a single thing across the whole application:
- the theme class map;
- the icon map;
- the toast queue;
- the pending confirmation.
It lives outside Vue and React so that any piece can notify without knowing which framework is behind it, whether that is a store, a model contract or the table. Its CSS variables define colors, shapes, density, layers and motion. Dark mode is defined there, once.
2. The components
- Theme and icons. Components read the theme and icons from form-core and subscribe to changes: a live
setTheme()orsetIcons()repaints what is already mounted. - Toasts and confirmations.
ToastRegionComponentandConfirmHostComponentrender form-core's queue. - Validation and masks. Controls write
data-validators, whichinnoboxrr-js-validatorreads. Masks are applied byinnoboxrr-maskjs.
3. The table
The datatables use TanStack Table v9 for state:
- visible columns;
- sorting;
- selection.
The menu, icons and skeletons come from the components, and the toasts from form-core. The framework-independent logic lives in src/table.js, which is byte-for-byte the same file in both packages.
4. The generated module
For each model, LaraPack writes src/models/<model>/index.js: the model contract. It is the same file for Vue and React: pure functions and calls through innoboxrr-http-request, routes through innoboxrr-route-resolver, text through innoboxrr-i18n and confirmations through form-core's confirmAction.
Around the contract, LaraPack generates:
- the table widget;
- the forms, built with the components and
innoboxrr-js-validator; src/theme.js, which imports form-core's styles;src/i18n.js, with the translations and the table labels;- the
npm run localescript, which runslocale-gen.
The details are in The generated UI.
What the module's package.json declares (in application mode, the application declares it):
| Vue | React | |
|---|---|---|
dependencies | innoboxrr-form-core ^2.8.0, innoboxrr-form-elements ^6.7.0, innoboxrr-vue-datatable ^3.1.0, innoboxrr-http-request ^2.0.0, innoboxrr-i18n ^1.2.0, innoboxrr-js-validator ^2.0.0, innoboxrr-route-resolver ^2.0.0 | innoboxrr-form-core ^2.8.0, innoboxrr-react-form-elements ^3.7.0, innoboxrr-react-datatable ^3.1.0, innoboxrr-http-request ^2.0.0, innoboxrr-i18n ^1.2.0, innoboxrr-js-validator ^2.0.0, innoboxrr-route-resolver ^2.0.0 |
peerDependencies | pinia ^3.0.0, vue ^3.5.0, vue-router ^4.5.0 | react ^19.0.0, react-dom ^19.0.0, react-router-dom ^7.0.0, zustand ^5.0.0 |
devDependencies | @vitejs/plugin-vue ^6.0.0, innoboxrr-locale-generator ^2.0.0, vite ^8.0.0 | @vitejs/plugin-react ^6.0.0, innoboxrr-locale-generator ^2.0.0, vite ^8.0.0 |
New releases arrive without regenerating
The ^ ranges accept 6.8.0, 3.8.0, 3.1.1 and 2.0.1. Running npm update in the application is enough to get them.
5. The shell
The base application boots in this order:
- Routes. It loads
routes.jsonwithsetRoutes(). - Languages. It adds the module's translations first and its own after them, and calls
setLocale()with thelangof<html>. - Styles. It imports form-core's styles and the module's
theme.js. - Toasts and confirmations. It mounts
ToastRegionComponentandConfirmHostComponentexactly once.
Dark mode is decided by a data-theme attribute on <html>. The full contract is in The interface contract.
Booting an application by hand
Without the base application, this is the minimum for the components, the table and the model contract to work. The ./routes.json and ./lang/*.json paths are examples.
import { createApp } from 'vue'
import 'innoboxrr-form-core/styles'
import FormElements from 'innoboxrr-form-elements'
import { setRoutes } from 'innoboxrr-route-resolver'
import { addTranslations, setLocale } from 'innoboxrr-i18n'
import routes from './routes.json'
import App from './App.vue'
setRoutes(routes)
addTranslations(import.meta.glob('./lang/*.json', { eager: true }))
setLocale(document.documentElement.lang)
createApp(App).use(FormElements).mount('#app')<template>
<RouterView />
<!-- Exactly once in the whole application -->
<ToastRegionComponent />
<ConfirmHostComponent />
</template>import { createRoot } from 'react-dom/client'
import 'innoboxrr-form-core/styles'
import 'innoboxrr-react-form-elements/src/css/form-elements.css'
import { ConfirmHostComponent, ToastRegionComponent } from 'innoboxrr-react-form-elements'
import { setRoutes } from 'innoboxrr-route-resolver'
import { addTranslations, setLocale } from 'innoboxrr-i18n'
import routes from './routes.json'
import App from './App.jsx'
setRoutes(routes)
addTranslations(import.meta.glob('./lang/*.json', { eager: true }))
setLocale(document.documentElement.lang)
createRoot(document.getElementById('app')).render(
<>
<App />
{/* Exactly once in the whole application */}
<ToastRegionComponent />
<ConfirmHostComponent />
</>
)What's new in this round
- innoboxrr-form-elements 6.8.0. The code editor loads its language on demand and follows the application theme.
autofocusnow defaults tofalse, which is a behavior change: details. - innoboxrr-react-form-elements 3.8.0. The code editor also loads its language on demand.
themeis stilldarkorlight. - innoboxrr-vue-datatable and innoboxrr-react-datatable 3.1.1. GET and HEAD requests no longer carry
_tokenin the query, andsetFilters()no longer receives it. POST still sends it: details. - innoboxrr-js-validator 2.0.1. Every message is inserted as text, including those from
appendExternalErrors. HTML in themessagesoption is now shown literally: details.
Vite and Vitest
There are two toolchains, and that's deliberate:
- The packages in this section are tested with Vite 7 and Vitest 3. Vitest 4 with Vite 8.3 does not install with npm 10, which is what Node 20 and 22 ship with in CI.
- Generated modules and applications use Vite 8, the one Laravel 13 uses. They don't install Vitest, so that blocker doesn't affect them.
Nothing changes for you: your application compiles the components with its own Vite 8. The full version table is in Versions and compatibility.