v3.1.1
Primeros Pasos

Laravel + Inertia

Integra Spartan en una aplicación Laravel con Inertia y Vue 3.

En un proyecto Laravel con Inertia, Vue vive dentro de resources/js y Vite compila los assets. Spartan se integra como cualquier dependencia de Vue: la diferencia está en dónde ubicar la hoja de estilos y dónde registrar el plugin de i18n.

Requisitos

  • Laravel 11 o 12 con Vite
  • Inertia con el adaptador @inertiajs/vue3
  • Vue ^3.5
  • TailwindCSS ^4 mediante el plugin @tailwindcss/vite
  • Node.js 20.19 o ^22.12
Los starter kits oficiales (laravel new eligiendo Vue) ya incluyen Inertia, Vue 3, TailwindCSS v4 y SSR configurados. Si partes de uno de ellos, solo necesitas los pasos de esta guía.

Instalación paso a paso

Instalar los paquetes

Spartan se instala como dependencia de desarrollo. vue-i18n es un peer dependency requerido: los componentes con texto interno lo usan para resolver sus traducciones.

npm install -D @placetopay/spartan-vue
npm install vue-i18n

No necesitas instalar @tailwindcss/forms ni vue-sonner: ambos son dependencias directas de Spartan y se resuelven solos.

Configurar los estilos

Laravel concentra los estilos en resources/css/app.css. Agrega el @import de Spartan justo después del de TailwindCSS:

resources/css/app.css
@import 'tailwindcss';
@import '@placetopay/spartan-vue/styles.css';

@source '../js/**/*.{vue,js,ts}';
@source '../views/**/*.blade.php';
Las rutas de @source son relativas al archivo CSS, no a la raíz del proyecto. Desde resources/css/ la ruta hacia tus componentes Vue es ../js/.
No hace falta un @source que apunte a node_modules. La hoja de estilos de Spartan ya declara internamente el escaneo de sus propios archivos compilados, así que sus clases se generan de forma automática.

Si tu proyecto viene de un starter kit, es probable que app.css ya traiga globs equivalentes (@source '../**/*.vue'). En ese caso basta con añadir el @import de Spartan.

Registrar vue-i18n en el entry point

El punto de entrada de Inertia es donde se crea la instancia de Vue, así que es ahí donde se registra el plugin de i18n:

resources/js/app.ts
import '../css/app.css';

import { createInertiaApp } from '@inertiajs/vue3';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { createApp, h, type DefineComponent } from 'vue';
import { createI18n } from 'vue-i18n';
import { addSpartanAllMessages } from '@placetopay/spartan-vue/i18n';

const i18n = createI18n({
    legacy: false,
    locale: 'es',
    fallbackLocale: 'en',
    messages: addSpartanAllMessages({}),
});

createInertiaApp({
    resolve: (name) =>
        resolvePageComponent(
            `./pages/${name}.vue`,
            import.meta.glob<DefineComponent>('./pages/**/*.vue'),
        ),
    setup({ el, App, props, plugin }) {
        createApp({ render: () => h(App, props) })
            .use(plugin)
            .use(i18n)
            .mount(el);
    },
});
legacy: false es obligatorio. Los componentes de Spartan usan useI18n() de la Composition API; con el modo legacy activo los textos internos quedan sin traducir.

addSpartanAllMessages({}) devuelve los cinco idiomas de Spartan listos para usar. Para mezclarlos con tus propias traducciones, pásalas como argumento:

resources/js/app.ts
import es from './lang/es.json';
import en from './lang/en.json';

const i18n = createI18n({
    legacy: false,
    locale: 'es',
    fallbackLocale: 'en',
    messages: addSpartanAllMessages({ es, en }),
});

Usar los componentes

Los componentes se importan directamente en tus páginas de Inertia:

resources/js/pages/Dashboard.vue
<script setup lang="ts">
import { ref } from 'vue';
import { router } from '@inertiajs/vue3';
import { SButton, SInputBlock } from '@placetopay/spartan-vue';

const name = ref('');

const submit = () => router.post('/teams', { name: name.value });
</script>

<template>
  <form @submit.prevent="submit">
    <SInputBlock v-model="name" label="Nombre del equipo" />
    <SButton type="submit">Crear</SButton>
  </form>
</template>
Si tus páginas viven en resources/js/Pages (con mayúscula, convención de Laravel Breeze), ajusta los globs de resolvePageComponent y de @source en consecuencia.

Configuración de Vite

Para que todo lo anterior funcione, vite.config.ts debe cargar los plugins de Laravel, TailwindCSS y Vue, e incluir la hoja de estilos entre los assets compilados:

vite.config.ts
import tailwindcss from '@tailwindcss/vite';
import vue from '@vitejs/plugin-vue';
import laravel from 'laravel-vite-plugin';
import { defineConfig } from 'vite';

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/js/app.ts'],
            ssr: 'resources/js/ssr.ts',
            refresh: true,
        }),
        tailwindcss(),
        vue(),
    ],
});

Server-Side Rendering

Si tu aplicación usa SSR, el entry point del servidor crea su propia instancia de Vue, por lo que también debe registrar el plugin de i18n:

resources/js/ssr.ts
import { createInertiaApp } from '@inertiajs/vue3';
import createServer from '@inertiajs/vue3/server';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { renderToString } from '@vue/server-renderer';
import { createSSRApp, h, type DefineComponent } from 'vue';
import { createI18n } from 'vue-i18n';
import { addSpartanAllMessages } from '@placetopay/spartan-vue/i18n';

createServer((page) =>
    createInertiaApp({
        page,
        render: renderToString,
        resolve: (name) =>
            resolvePageComponent(
                `./pages/${name}.vue`,
                import.meta.glob<DefineComponent>('./pages/**/*.vue'),
            ),
        setup({ App, props, plugin }) {
            const i18n = createI18n({
                legacy: false,
                locale: 'es',
                fallbackLocale: 'en',
                messages: addSpartanAllMessages({}),
            });

            return createSSRApp({ render: () => h(App, props) })
                .use(plugin)
                .use(i18n);
        },
    }),
);
Crea la instancia de createI18ndentro de setup, no a nivel de módulo. En SSR el proceso atiende varias peticiones y una instancia compartida filtra el idioma de un usuario a otro.

Modo oscuro

Spartan activa el modo oscuro con la estrategia de clase .dark. Añádela al elemento raíz de tu plantilla Blade:

resources/views/app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
<head>
    @vite(['resources/js/app.ts', 'resources/css/app.css'])
    @inertiaHead
</head>
<body>
    @inertia
</body>
</html>

Para alternarlo en tiempo de ejecución, agrega o quita la clase dark sobre document.documentElement.

Personalización del tema

La paleta primaria se sobrescribe con variables CSS dentro de @theme, siempre después del @import de Spartan:

resources/css/app.css
@import 'tailwindcss';
@import '@placetopay/spartan-vue/styles.css';

@source '../js/**/*.{vue,js,ts}';
@source '../views/**/*.blade.php';

@theme {
  --color-spartan-primary-50:  rgb(228 242 253);
  --color-spartan-primary-500: rgb(33 150 243);
  --color-spartan-primary-900: rgb(13 42 84);
}

Consulta la guía de instalación para la paleta completa y para excluir la tipografía Inter.

Problemas comunes

Los estilos no se aplican

Revisa que resources/css/app.css esté declarado en el @vite de tu plantilla Blade y que los globs de @source cubran resources/js. Recuerda que esas rutas son relativas al archivo CSS.

Las clases aparecen en desarrollo pero no en producción

Vite purga clases no detectadas al compilar. Si un componente recibe clases construidas dinámicamente en PHP o Blade, añade un @source que cubra esos archivos, o declara las clases con @source inline(...).

Los textos internos de los componentes no se traducen

Confirma que el plugin de i18n esté registrado en app.ts (y en ssr.ts si usas SSR) y que legacy esté en false. Más detalle en la guía de i18n.

Errores de resolución con pnpm

Desde 3.0.0-beta.17 Spartan declara todas sus dependencias, por lo que funciona con el node_modules estricto de pnpm. Si estás fijado a una versión anterior, añade shamefully-hoist=true a tu .npmrc.

Copyright © 2026