---
title: Getting Started
description: Install WireKit in a Laravel + Livewire project — the verified-working recipe used by docs.wirekit.app itself
visibility: guest
draft: false
---

# Getting Started

This is the recipe used by `docs.wirekit.app` itself. Following it gets you to a working install with every component rendering, every Alpine-driven interaction working, no console errors, and `php artisan wirekit:doctor` reporting green.

::: tip Recommended setup
**Laravel 12+/13+ with Livewire v4+, Vite, and Tailwind CSS v4** — this is the configuration WireKit is built and tested against on every commit. Other setups (Livewire v3, Tailwind v3, Inertia, no Vite) may work but receive no continuous-integration coverage and are unsupported. If you're starting a fresh project, follow the "From scratch" recipe below verbatim.
:::

::: info Already using the Livewire Starter Kit?
Your Starter Kit project ships with its own pre-configured layout, Vite config, and Tailwind v4 setup — adding WireKit on top requires touching a few specific files instead of following the clean-room recipe. See **[Adding WireKit to the Livewire Starter Kit](/getting-started/livewire-starter-kit)** for the four-step retrofit.
:::

## Prerequisites

Before installing WireKit, your Laravel app needs **Laravel 12+/13+ with Livewire v4+ installed and a working Vite + Tailwind CSS v4 toolchain**. If your app already meets these, skip to [Installation](#installation).

### From scratch — Laravel + Livewire + Tailwind v4

```bash
# 1. Create a Laravel app
composer create-project laravel/laravel my-app
cd my-app

# 2. Add Livewire v4 (provides Alpine.js automatically — no separate npm install needed)
composer require livewire/livewire:^4.0

# 3. Add Tailwind CSS v4 + Vite plugin
npm install tailwindcss @tailwindcss/vite
```

Wire Tailwind into Vite — `vite.config.js`:

```javascript
// 1. Vite is Laravel 12's default asset bundler — defineConfig + the
//    laravel-vite-plugin wire your Blade @vite directive to a dev /
//    build pipeline.
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

// 2. Tailwind v4 ships as a first-class Vite plugin — no PostCSS config
//    file needed, no tailwind.config.js required.
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
    plugins: [
        // 3. Register the CSS + JS entry points Vite watches. `refresh: true`
        //    triggers a Livewire-aware page-reload on Blade / route changes.
        laravel({ input: ['resources/css/app.css', 'resources/js/app.js'], refresh: true }),
        // 4. Activate Tailwind's compiler — utilities are scanned out of
        //    your views at build time.
        tailwindcss(),
    ],
});
```

Replace `resources/css/app.css` with the v4 entrypoint:

```css
@import 'tailwindcss';
```

Verify the toolchain runs cleanly:

```bash
npm run dev
```

Vite should start without errors. Stop with `Ctrl+C` once you see the green "ready" banner.

::: warning Don't skip the next step
At this point your Tailwind v4 toolchain is wired but your project has no WireKit yet — the next two commands must run before any production build. WireKit components emit Tailwind utility classes; without registering WireKit's Blade templates as a Tailwind `@source`, those classes get tree-shaken at build and the components render unstyled. `wirekit:install` writes the `@source` line for you. If you run `npm run dev` between steps 1 and 4 below and see unstyled output, that's the symptom.
:::

## Installation

```bash
# 1. Add WireKit + the two icon packages it relies on at runtime
#    (pushery/wirekit = the component library itself,
#     blade-ui-kit/blade-icons = the icon-renderer <x-wirekit::icon> calls,
#     blade-ui-kit/blade-heroicons = the heroicons preset that ships the actual SVGs)
composer require pushery/wirekit blade-ui-kit/blade-icons blade-ui-kit/blade-heroicons

# 2. Run the WireKit installer — publishes config/wirekit.php, copies the
#    asset bundles to public/vendor/wirekit/, and adds the Tailwind @source
#    line for WireKit's Blade templates to resources/css/app.css
php artisan wirekit:install
```

`wirekit:install` does everything in one shot — publishes `config/wirekit.php`, publishes the assets to `public/vendor/wirekit/`, and adds the `@source` line for WireKit Blade templates to `resources/css/app.css`.

> **Why install icon packages by default?** `<x-wirekit::icon>` is used internally by buttons, dropdowns, modals, alerts, and the close-button on most overlays. Without `blade-ui-kit/blade-icons` + a preset (`blade-heroicons` is the safest default), icons fall back to inert placeholders. They don't crash, but the UX is missing the visual cues. Installing both alongside WireKit keeps the experience complete from day one.

## Layout setup — the verified-working pattern

The exact layout shape used by `docs.wirekit.app`. Two critical points:

1. **Both directives at the end of `<body>`**, in this order: `@wirekitScripts` THEN `@livewireScripts`. Always emit both, never gate them on "do I use Livewire on this page?".
2. **Alpine arrives via `@livewireScripts`** — Livewire v4 bundles Alpine, you do NOT install `alpinejs` via npm and you do NOT import it in `app.js`.

Two layout paths are first-class on a Livewire 4 install — both work; which one applies depends on how your project was created:

**Fresh app (no Starter Kit)** — `resources/views/layouts/app.blade.php`. This is the path `php artisan livewire:layout` creates, because Livewire 4's default page layout is the `'layouts::app'` namespace (which resolves to `resources/views/layouts/`). Reference it with `#[Layout('layouts.app')]`, or omit the attribute entirely since `'layouts::app'` is already Livewire's default.

**Starter Kit app** — `resources/views/components/layouts/app.blade.php`. The Laravel Starter Kits scaffold this path (it matches Laravel's `<x-layouts.app>` component shape). If your app already ships it, add the WireKit directives there and reference it with `#[Layout('components.layouts.app')]`.

`wirekit:install` writes the `@wirekitStyles` + `@wirekitScripts` directives into your layout. If no layout exists yet — a fresh Laravel + Livewire 4 app (without a Starter Kit) ships none — the installer creates one for you via Livewire's own `php artisan livewire:layout` command, which produces `resources/views/layouts/app.blade.php`, then injects the directives (in the correct order, see below). So a clean install needs no manual layout step — just reference that layout with `#[Layout('layouts.app')]`, or leave the attribute off. You can also create the layout yourself first (`php artisan livewire:layout`, or hand-write the file below) and re-run the installer to wire it. `wirekit:doctor` honors both canonical paths — it scans every `*.blade.php` file under either. The example below shows the fresh-app path (`resources/views/layouts/app.blade.php`) with the directives already in place:

```blade
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ $title ?? 'My App' }}</title>

    @vite(['resources/css/app.css', 'resources/js/app.js'])

    {{-- WireKit's design tokens — the --color-wk-* CSS variables every  --}}
    {{-- component reads for its colors, spacing, radii, and shadows.    --}}
    {{-- Without this line components render against undefined tokens     --}}
    {{-- (i.e. unstyled). wirekit:install adds it for you; it is shown    --}}
    {{-- here so the layout is complete on its own. (If you instead       --}}
    {{-- @import wirekit.css inside app.css — see "Two valid setup        --}}
    {{-- paths for CSS" below — drop this line so tokens don't load twice.)--}}
    @wirekitStyles
</head>
<body>

{{ $slot }}

{{-- Critical order: @wirekitScripts BEFORE @livewireScripts --}}
{{-- @livewireScripts boots Alpine; WireKit's Alpine components must  --}}
{{-- register on the alpine:init event BEFORE Alpine starts.          --}}
@wirekitScripts
@livewireScripts

</body>
</html>
```

> **Dark mode.** WireKit components are dark-mode aware via `--color-wk-*` design tokens that auto-switch when `<html>` carries the class `dark`. Toggle it with any pattern you like (a button + Alpine `x-data`, a server-side preference check, `prefers-color-scheme` MediaQuery — all work). For the simplest possible activation, hardcode `<html lang="en" class="dark">` and refresh.

### Two valid setup paths for CSS

**`wirekit:install` already wired path 1 for you** — it added `@wirekitStyles` to your layout's `<head>` (shown above), so a fresh install is already loading WireKit's tokens. Read this section only if you'd rather use path 2 — piping WireKit's CSS through your own Tailwind build, the way `docs.wirekit.app` does. If you switch to path 2, remove `@wirekitStyles` from the layout so the tokens don't load twice.

> **Two valid setup paths for CSS:**
>
> 1. **`@wirekitStyles` Blade directive in `<head>`** — emits a `<link>` tag straight to the package CSS. Fastest path, no Tailwind compile step required for variables.
> 2. **`@import 'wirekit.css'` in `app.css`** — pipes through the Tailwind v4 bundler so utilities defined inside WireKit get optimized together with your own. This is what `docs.wirekit.app` uses:
>
>    ```css
>    /* resources/css/app.css */
>    @import 'tailwindcss';
>    @import '../../public/vendor/wirekit/wirekit.css';
>    @source '../../vendor/pushery/wirekit/resources/views/**/*.blade.php';
>    ```
>
>    Pick whichever fits your build pipeline. Both render byte-identical CSS at runtime.

## JavaScript pipeline — keep it minimal

**In a standard Laravel app there is nothing to do here.** A fresh `composer create-project laravel/laravel` (the "From scratch" recipe above) already scaffolds both `resources/js/app.js` and `resources/js/bootstrap.js`, and the two blocks below simply show what Laravel writes into them by default. WireKit adds **nothing** to either file. The single point of this section: `app.js` must NOT import Alpine — Livewire v4 ships Alpine in its bundle and starts it for you when `@livewireScripts` runs, so a manual `import Alpine from 'alpinejs'` would boot Alpine twice and break every WireKit interaction.

::: warning Do NOT create `bootstrap.js` if your project doesn't have one
Whether the file exists depends on how your project was scaffolded, and **the missing-file case is not an invitation to create it**:

- **`composer create-project laravel/laravel`** ships `resources/js/app.js` (holding the single `import './bootstrap';` line) *and* `resources/js/bootstrap.js`, *and* lists `axios` in `package.json`. Nothing to do — confirm `app.js` has no Alpine import and move on. The two blocks below are shown only so you can recognize what's already there.
- **The Livewire Starter Kit** ships `app.js` alone. There is no `bootstrap.js`, and **`axios` is not in its `package.json`**. Writing the `bootstrap.js` block below into such a project fails the very next build with `[vite]: Rolldown failed to resolve import "axios"` (or `Rollup failed to resolve import "axios"` on Vite 7 and earlier), because the import names a package that was never installed.

**WireKit uses no axios, so on a project without it the correct action is to create nothing.** The blocks below are Laravel's own convention, not a WireKit requirement. If you want that baseline anyway, install the package first:

```bash
# Only needed if you actually want Laravel's axios baseline AND your
# scaffold didn't already install it (the Livewire Starter Kit does not).
npm install axios
```

:::

```javascript
// resources/js/app.js — as the Laravel skeleton writes it
//
// 1. Single entry point loaded by Vite via the @vite directive in the
//    Blade layout. The skeleton keeps this file as one import statement
//    so future additions stay obvious. Delete the line if you have no
//    bootstrap.js — an import of a file that doesn't exist fails the
//    build just like a missing package does.
import './bootstrap';
```

```javascript
// resources/js/bootstrap.js — Laravel convention, NOT a WireKit requirement.
// Requires `axios` in package.json (see the box above before copying this).
//
// 1. Make axios available globally as `window.axios`, the way Laravel's
//    own skeleton wires it. This is for YOUR OWN request code —
//    Livewire does not read window.axios; it issues its own fetch()
//    calls. Nothing in WireKit or Livewire breaks without this file.
import axios from 'axios';
window.axios = axios;

// 2. Tag every axios request as an XMLHttpRequest so Laravel's
//    `Request::expectsJson()` returns true for the AJAX calls you make
//    through axios. It has no effect on Livewire's own requests.
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
```

That's it. No `import Alpine from 'alpinejs'`, no `Alpine.start()`, no Alpine version pinning in `package.json`. Custom Alpine components register via `Alpine.data(name, fn)` inside `document.addEventListener('alpine:init', …)` listeners loaded with `defer` — they fire BEFORE `@livewireScripts` boots Alpine because `defer` scripts execute in document order.

## Your first Livewire page

A complete, copy-pasteable example wiring up a Livewire page-component, a route, and a `wire:model`-bound form input on the layout above.

**Run the command with `--class`** — the flag is what decides which files you get, so it is not optional here. `php artisan make:livewire Showcase --class` generates an empty `app/Livewire/Showcase.php` class plus a placeholder `resources/views/livewire/showcase.blade.php` view: the two files this walkthrough edits. **Leave the flag off and neither file exists** — Livewire 4 defaults to a single-file component and writes `resources/views/components/⚡showcase.blade.php` instead, so every path referenced below would be missing.

Either way the command generates only the **file structure** — it does NOT write the example code for you. After running it, open each generated file and **replace its stub contents** with the corresponding block shown below (the PHP class, then the Blade view). The `routes/web.php` line you add by hand.

```bash
# 1. Generate the Livewire component. Livewire 4 creates a SINGLE-FILE component
#    by default (resources/views/components/⚡showcase.blade.php — the ⚡ prefix is
#    intentional, it marks Livewire components in your file tree). This walkthrough
#    uses the class-based form, so pass --class: that creates
#    app/Livewire/Showcase.php + resources/views/livewire/showcase.blade.php. Both
#    arrive as empty stubs (a placeholder <div> + a bare render() method) — that's
#    normal; replace their generated contents with the two files shown below.
#    Prefer class-based everywhere? Set 'type' => 'class' in config/livewire.php
#    and you can drop the flag.
php artisan make:livewire Showcase --class
```

```php
<?php
// app/Livewire/Showcase.php

namespace App\Livewire;

use Livewire\Component;

class Showcase extends Component
{
    // No #[Layout(...)] attribute is needed here: Livewire wraps a full-page
    // component in its default layout (the 'layouts::app' view, i.e.
    // resources/views/layouts/app.blade.php) — the exact file wirekit:install
    // created for you. To pin it explicitly, add `use Livewire\Attributes\Layout;`
    // plus #[Layout('layouts.app')] above the class. (Starter Kit apps reference
    // the `components.layouts.app` view instead — see Layout setup above.)

    // Public properties become two-way bindings for any wire:model in the view.
    public string $email = '';

    public function render()
    {
        return view('livewire.showcase');
    }
}
```

```blade
{{-- resources/views/livewire/showcase.blade.php --}}
<div class="p-8 space-y-4">
    <x-wirekit::button>Save</x-wirekit::button>

    <x-wirekit::input
        label="Email"
        type="email"
        name="email"
        wire:model="email"
    />

    <x-wirekit::select
        label="Role"
        name="role"
        placeholder="Choose a role..."
        :options="['admin' => 'Admin', 'editor' => 'Editor', 'viewer' => 'Viewer']"
    />

    <x-wirekit::dropdown>
        <x-slot:trigger>
            <x-wirekit::button>Open Menu</x-wirekit::button>
        </x-slot:trigger>
        <x-wirekit::dropdown.item>Profile</x-wirekit::dropdown.item>
        <x-wirekit::dropdown.item>Settings</x-wirekit::dropdown.item>
    </x-wirekit::dropdown>
</div>
```

```php
// routes/web.php
use App\Livewire\Showcase;

// A fresh Laravel app already maps `/` to the welcome page:
//     Route::get('/', function () { return view('welcome'); });
// REPLACE that existing line with the one below — don't just add a
// second `/` route. When two routes share the same URI, the one
// registered LAST wins, so a leftover welcome route sitting below this
// one would silently shadow your component and you'd still see the
// Laravel welcome screen.
//
// Pointing a route at a Livewire component renders it as a full page,
// wrapped automatically by Livewire's default layout (or the
// #[Layout(...)] you set on the component above). WireKit never touches
// your routes — adding this line is yours to do.
Route::get('/', Showcase::class);
```

## Run the app

WireKit needs both the **PHP server** (Laravel) and the **Vite dev server** (CSS + JS bundling) running side by side. Two terminals:

```bash
# Terminal 1 — Laravel HTTP server (serves PHP routes on http://127.0.0.1:8000)
php artisan serve
```

```bash
# Terminal 2 — Vite dev server (compiles app.css + app.js, hot-reloads on save)
npm run dev
```

Open <http://127.0.0.1:8000> in your browser. The four WireKit components render styled and interactive: the dropdown opens, the inputs validate, the button hovers, the email field round-trips its value to the Livewire backend on every keystroke. Alpine has booted via Livewire; WireKit's Alpine components have registered on `alpine:init`.

If your fresh Laravel install needs a database touch (some `wire:model` round-trips persist via Eloquent later), run:

```bash
# 1. Create the SQLite file. Laravel 12 defaults to SQLite but ships no database file,
#    and the driver will not create one for you — the first query fails without this.
touch database/database.sqlite

# 2. Apply the migrations. This builds the tables your Livewire component writes to;
#    until it runs, a wire:model round-trip that persists will fail on a missing table.
php artisan migrate
```

## Build for production

`npm run dev` above is Vite's development server (hot reload) for local work. For a
production deploy — and so the `wirekit:doctor` "Built app CSS contains WireKit
utility rules" check below has a compiled bundle to read — run the optimized build:

```bash
# 1. Compile app.css + app.js for production. wirekit:install added the `@source`
#    line, so this build is what bakes WireKit's utility classes into your
#    compiled CSS — without it a production deploy renders unstyled.
npm run build
```

::: info Dev versus build
For local development `npm run dev` is enough; you only need `npm run build` for a
real deploy. Both modes read WireKit's `@source` line, so components are styled
either way. (`wirekit:doctor`'s built-CSS check stays skipped until a `npm run
build` has produced `public/build/`.)
:::

## Verify the install

```bash
php artisan wirekit:doctor
```

A green install prints a `✓` for every check and ends with the totals line and
`All checks passed.` The full annotated transcript, the glyph vocabulary and what each
check means live on the command's own page:
[`wirekit:doctor`](cli-reference/wirekit-doctor.md#example-output).

If a check fails, the doctor prints the exact fix instruction. The verified-working pattern above clears every check.

## Change the prefix

By default, components use the `wirekit` prefix: `<x-wirekit::button>`. To change:

```bash
php artisan vendor:publish --tag=wirekit-config
```

```php
// config/wirekit.php
'prefix' => 'ui', // Now use <x-ui::button>
```

Code that builds a tag name — a page generator, a CMS field, a custom Artisan
command — should ask rather than assume, because the prefix is a setting and
`wirekit` is only its default:

```php
use Pushery\WireKit\WireKit;

$tag = '<x-'.WireKit::prefix().'::button>';
```

## JavaScript bundle (full vs core)

The `@wirekitScripts` directive serves the **full** bundle by default, which contains every Alpine component including overlays (Dropdown, Tooltip, Modal, Drawer, etc.) and the reading-* family. Current sizes for every bundle are in [Dependencies](/dependencies).

If you only use form components and charts, switch to the **core** bundle in `config/wirekit.php`:

```php
'scripts' => ['bundle' => 'core'],
```

Core ships only the chart Alpine component, and is a fraction of the full bundle.

## Optional: publish assets for production

For better performance (web server serving instead of PHP routes), publish the CSS and JS:

```bash
php artisan vendor:publish --tag=wirekit-assets
```

This copies `wirekit.css` and the JS bundles to `public/vendor/wirekit/`. The `@wirekitStyles` and `@wirekitScripts` directives auto-detect published assets and prefer them.

> **Cache busting is automatic.** Both directives append `?v={filemtime}` to every URL, so browsers fetch fresh content on every deploy. Even if you forget `vendor:publish --tag=wirekit-assets --force` after a `composer update`, WireKit detects the stale published copy and transparently falls back to serving the fresh version via its built-in route — you'll never see outdated CSS or JS in the browser. See [Integration → Asset Publishing](/getting-started/integration#asset-publishing) for the full story.

## Next Steps

- [Theming](/theming) — Change colors without touching a single component
- [Button](/components/button) — Your first component
- [Dropdown](/components/dropdown) — Your first overlay component
- [Integration Guide](/getting-started/integration) — Asset publishing, optional dependencies, verification checklist

## Optional Features

Each of these is opt-in, lives behind its own config flag, and has a dedicated docs page with the full setup walk-through. The links below are the canonical reference — read them when you actually need the feature.

- **[Icons](/components/icon)** — swap or stack the icon preset (heroicons / lucide / phosphor / tabler), set up the alias system, customize per-component icon defaults
- **[Fonts](/components/fonts)** — load GDPR-compliant local fonts (no Google Fonts CDN), pick a sans / serif / mono pairing, override per-component
- **[Chart](/components/chart)** — opt-in Chart.js integration with automatic theming, custom adapters, dark-mode aware data colors. **Three steps**: set `'charts.library' => 'chartjs'` in `config/wirekit.php`, `npm install chart.js`, AND add the `Chart.register(...registerables)` snippet to `resources/js/app.js` per [Integration → Optional Dependencies](/getting-started/integration#optional-dependencies). Skipping the JS-side registration is the #1 first-run gotcha — `wirekit:doctor` flags it explicitly.
- **[QR Code](/components/qr-code)** — render scannable QR codes server-side via `bacon/bacon-qr-code` (Composer package, not auto-required). See [Integration → QR Code](/getting-started/integration#qr-code-for-x-wirekitqr-code) for the one-line install + the trade-offs vs. JavaScript-based generators.
