Appearance
Editions
An edition is a Laravel application that installs the eight laraspring/* packages and draws their screens. Two ship, and both are complete: the same backend, the same routes, the same tests, with the interface written in a different frontend.
React edition (apps/saas) | Vue edition (apps/vue) | |
|---|---|---|
| Stack | Laravel 12 + Inertia 2 + React 19 + shadcn/ui | Laravel 12 + Inertia 2 + Vue 3 + shadcn-vue |
| Screens | all of them | all of them |
| A page | resources/js/pages/projects/index.tsx | resources/js/pages/projects/index.vue |
| A component | components/upload-field.tsx | components/UploadField.vue |
| Strings | useTranslations() from hooks/use-translations.ts | useTranslations() from composables/useTranslations.ts |
| Typecheck | npm run types (tsc) | npm run types (vue-tsc) |
You buy one of them. It arrives as a repository of its own, with the packages, the documentation, the agent skills and bin/setup inside it, and the other edition is not in it.
What they share
Everything that is not a screen. The renderers, the registries, the User model, the migrations, the routes, the language files, config/brand.php, config/demo.php, the seeders and the controllers are the same files with the same names in both editions. So is the test suite, except for the handful of assertions about which file a screen name draws.
That is a rule rather than an observation, and it is the one that keeps a second edition from becoming a second product: a change to the PHP side of one edition belongs in the other in the same commit. If a change cannot be made in both, it is presentation and belongs in resources/js.
Practically, it means the documentation on this site applies to both. Where a page names a path under apps/saas, the Vue edition has the same file under apps/vue, with .tsx becoming .vue and kebab-case component files becoming PascalCase ones.
Page names are identical too. The string a package's renderer passes to Inertia::render() is projects/index in both, kebab-case, because a test asserts on it. Only the extension of the file it draws differs.
What differs
Only resources/js. Beyond the file extension, one thing genuinely reads differently, and it will catch you once:
In the Vue edition, useTranslations() returns locale, fallback and available as computed refs, so they need .value inside a <script setup> block and unwrap by themselves in a <template>. t() and list() are identical in both. A React hook runs again on every render and a Vue composable does not, so a plain value there would freeze at whatever was shared when the component was created, which is a currency formatted in yesterday's language.
Choosing
Pick the one your team writes every day. Neither is the "lite" one, and there is no feature behind the other.
Two tie-breakers if you have no preference:
- The React edition is the reference. New screens are drawn there first and the Vue edition follows, so between releases it is occasionally a day ahead. The documentation, the agent skills and the demo seed are written against it.
- The Vue edition uses shadcn-vue, which is a smaller component library than shadcn/ui. If you expect to lean hard on prebuilt primitives, check that the ones you want exist before you commit.
Everything else, the packages, the API, the upgrade path and the price, is the same.