chipslays / vue
The skeleton application for the Laravel framework.
Requires
- php: ^8.3
- inertiajs/inertia-laravel: ^3.0
- laravel/framework: ^13.0
- laravel/tinker: ^3.0
- tightenco/ziggy: ^2.6
Requires (Dev)
- fakerphp/faker: ^1.23
- laravel/pail: ^1.2.5
- laravel/pint: ^1.27
- mockery/mockery: ^1.6
- nunomaduro/collision: ^8.6
- pestphp/pest: ^4.4
- pestphp/pest-plugin-laravel: ^4.1
README
A modern starter kit for building full-stack applications with Laravel, Inertia.js, Vue 3, and TypeScript.
Pre-configured with dark mode, flash toasts, typed routes, state management, and a clean project structure — ready to build on top of.
Tech Stack
| Layer | Technology |
|---|---|
| Backend | Laravel, PHP 8.2+ |
| Frontend | Vue 3, TypeScript |
| Routing | Inertia.js v3, Wayfinder (typed routes) |
| Styling | Tailwind CSS v4, shadcn-vue (new-york/reka) |
| State | Pinia + pinia-plugin-persistedstate |
| Toasts | vue-sonner |
| Icons | lucide-vue-next |
| Date | dayjs (Russian locale) |
Features
- Dark Mode — light / dark / system with OS-level listener, persisted in localStorage
- Flash Toasts — Laravel
session('flash')messages automatically shown as toasts - Confirm Dialog — programmatic
await confirm()pattern via Promise - Typed Routes — auto-generated by Wayfinder, no manual route strings
- Auth Helpers —
useUser()composable with roles, permissions,can()/cannot(),is()/isNot() - Auth Components —
<Authenticated>and<Guest>render-slot components - Scope Component — inline reactive state without a store
- Utility Helpers —
plural(),truncate(),formatNumber(),formatCurrency(),formatDate(),copyToClipboard(),dataGet(),uid(),sleep() - Pinia Persistence — app and theme stores persisted out of the box
- CSS Architecture — split into
theme.css,base.css,components.css,utilities.css
Requirements
- PHP 8.3+
- Composer
- Node.js 20+
- npm
Installation
# Clone the repository git clone https://github.com/chipslays/vue.git my-app cd my-app # Install PHP dependencies composer install # Install Node dependencies npm install # Configure environment cp .env.example .env php artisan key:generate # Run database migrations php artisan migrate # Start development servers composer run dev
The
VITE_APP_NAMEvariable in.envcontrols the page<title>.
Project Structure
resources/js/
├── app.ts # Entry point: Inertia, Pinia, dayjs setup
├── components/
│ ├── Scope.vue # Inline reactive state via scoped slot
│ ├── Toaster.vue # Pre-styled vue-sonner toaster
│ └── Auth/
│ ├── Authenticated.vue # Renders slot only for authenticated users
│ └── Guest.vue # Renders slot only for guests
├── composables/
│ ├── useApp.ts # Access to the app store
│ ├── useConfirm.ts # Programmatic confirm dialog (Promise-based)
│ ├── useFlash.ts # Flash messages + auto-toast watcher
│ ├── usePageProps.ts # Typed access to Inertia page props
│ ├── useTheme.ts # Theme toggle (light/dark/system cycle)
│ └── useUser.ts # Current user, roles, permissions, guards
├── layouts/
│ ├── Application.vue # Main app layout (with Toaster)
│ └── Guest.vue # Guest layout (with Toaster)
├── lib/
│ ├── helpers.ts # Utility functions (plural, truncate, format...)
│ └── utils.ts # cn() — Tailwind class merger (shadcn-vue)
├── pages/
│ └── Welcome.vue # Demo page with theme toggle & Scope example
├── routes/ # ⚠ Auto-generated by Wayfinder — do not edit
├── stores/
│ ├── appStore.ts # Global app state (extend as needed)
│ └── themeStore.ts # Theme state with system preference listener
└── wayfinder/ # ⚠ Auto-generated by Wayfinder — do not edit
Composables
useUser()
Access the authenticated user from Inertia shared props.
const { user, isAuthenticated, isGuest, can, hasRole, initials } = useUser();
| Property | Type | Description |
|---|---|---|
user |
ComputedRef<User | null> |
Current user object |
id |
ComputedRef<number | string | null> |
User ID |
isAuthenticated |
ComputedRef<boolean> |
Whether the user is logged in |
isGuest |
ComputedRef<boolean> |
Whether the user is a guest |
isEmailVerified |
ComputedRef<boolean> |
Whether the email is verified |
initials(length?) |
() => string | null |
User name initials |
roles |
ComputedRef<string[]> |
User roles array |
permissions |
ComputedRef<string[]> |
User permissions array |
hasRole(...roles) |
() => boolean |
Has any of the given roles |
hasAllRoles(...roles) |
() => boolean |
Has all of the given roles |
can(permission) |
() => boolean |
Has the permission |
cannot(permission) |
() => boolean |
Does not have the permission |
is(otherUser) |
() => boolean |
Same user by ID |
get(path, default?) |
() => T | null |
Dot-notation access to user fields |
useTheme()
Cycle through light → dark → system themes.
const { currentTheme, isDark, toggleTheme, setTheme } = useTheme();
useConfirm()
Promise-based confirm dialog.
const { confirm } = useConfirm(); const ok = await confirm({ title: 'Delete record?', message: 'This action cannot be undone.', variant: 'danger', }); if (ok) { // proceed }
useFlash() / useFlashToasts()
Access Laravel flash messages. Call useFlashToasts() once in your layout to auto-show toasts.
// In a layout: useFlashToasts(); // In a component: const { has, get, flash } = useFlash(); if (has('success')) console.log(get('success'));
Laravel side:
return back()->with('flash', ['success' => 'Saved!']);
usePageProps()
Typed access to Inertia page props.
const { prop } = usePageProps(); const appName = prop<string>('appName', 'Laravel');
Components
<Scope>
Inline reactive state without creating a store or ref in the parent.
<Scope :data="{ count: 0 }" v-slot="{ data }"> <button @click="data.count++">{{ data.count }}</button> </Scope>
<Authenticated> / <Guest>
Conditional rendering based on auth state. Slot props are bound from useUser().
<Authenticated v-slot="{ user }"> <p>Hello, {{ user.name }}</p> </Authenticated> <Guest> <p>Please log in</p> </Guest>
Wayfinder (Typed Routes)
The resources/js/routes/ and resources/js/wayfinder/ directories are auto-generated by Wayfinder. Do not edit them manually.
To regenerate after adding/changing Laravel routes:
php artisan wayfinder:generate
Wayfinder runs automatically during npm run dev.
CSS Architecture
Styles are split into focused files imported in resources/css/app.css:
| File | Purpose |
|---|---|
theme.css |
Theme variables and custom animations |
base.css |
Global base styles (body, scrollbar, focus resets) |
components.css |
Component-level styles (NProgress bar) |
utilities.css |
Custom Tailwind utilities (scrollbar-none) |
PHP Helpers
Global helper functions available server-side (via app/helpers.php):
user($guard?)— get the authenticated userplural($n, $forms, $includeNumber)— Russian pluralization
License
Open-source under the MIT license.