Skip to content

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

PackageVersionVue / ReactWhat it doesPeer dependencies
innoboxrr-form-core2.8.0Both, framework-freeClass theme, --fe-* CSS variables, icons, toasts and confirmations, files, time zones
innoboxrr-form-elements6.8.0VueThe 37 components: form controls and desktop piecesvue ^3.5.0, sortablejs ^1.14.0, lightvue (optional)
innoboxrr-react-form-elements3.8.0ReactThe same 37 components, with the same namesreact ^19.0.0, react-dom ^19.0.0
innoboxrr-vue-datatable3.1.1VueThe admin listing over the model contractinnoboxrr-form-core ^2.6.0, innoboxrr-form-elements ^6.4.0, vue ^3.5.0, vue-router ^4.5.0
innoboxrr-react-datatable3.1.1ReactThe same listinginnoboxrr-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-request2.0.0Both; separate Vue pluginmakeHttpRequest with retries, confirmation and cancellationvue ^3.5.0 (optional)
innoboxrr-route-resolver2.0.0Bothroute(name, params) over Laravel's routes.json
innoboxrr-i18n1.2.0Botht(), addTranslations, setLocale
innoboxrr-js-validator2.0.1BothValidates forms from data-validators
innoboxrr-locale-generator2.0.0Node CLIlocale-gen: extracts t('…') keys into locales/*.json
innoboxrr-maskjs2.0.0Both; ./vue and ./react adaptersInput masksvue ^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

text
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 confirmations

1. 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() or setIcons() repaints what is already mounted.
  • Toasts and confirmations. ToastRegionComponent and ConfirmHostComponent render form-core's queue.
  • Validation and masks. Controls write data-validators, which innoboxrr-js-validator reads. Masks are applied by innoboxrr-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 locale script, which runs locale-gen.

The details are in The generated UI.

What the module's package.json declares (in application mode, the application declares it):

VueReact
dependenciesinnoboxrr-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.0innoboxrr-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
peerDependenciespinia ^3.0.0, vue ^3.5.0, vue-router ^4.5.0react ^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:

  1. Routes. It loads routes.json with setRoutes().
  2. Languages. It adds the module's translations first and its own after them, and calls setLocale() with the lang of <html>.
  3. Styles. It imports form-core's styles and the module's theme.js.
  4. Toasts and confirmations. It mounts ToastRegionComponent and ConfirmHostComponent exactly 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.

js
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')
vue
<template>
    <RouterView />

    <!-- Exactly once in the whole application -->
    <ToastRegionComponent />
    <ConfirmHostComponent />
</template>
jsx
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. autofocus now defaults to false, which is a behavior change: details.
  • innoboxrr-react-form-elements 3.8.0. The code editor also loads its language on demand. theme is still dark or light.
  • innoboxrr-vue-datatable and innoboxrr-react-datatable 3.1.1. GET and HEAD requests no longer carry _token in the query, and setFilters() 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 the messages option 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.