Skip to content
LarawellUi

Installation

LarawellUi copies components into your app, then gets out of the way. Everything below takes about five minutes.

Requirements

  • PHP 8.3 or later, and Laravel 12 or 13.
  • Vite and Tailwind CSS 4.1 or later. New Laravel apps have both.
  • Pages rendered with Blade, Livewire components included.
  • The date picker, date range picker, time picker and price roll need PHP's intl extension.

Browsers

  • Everything works as designed in Chrome and Edge 117, Firefox 129 and Safari 17.5 or later, all released in 2024.
  • In Chrome and Edge 114 to 116, Firefox 128 and Safari 17 to 17.4, everything works, but some opening and closing animations are skipped.
  • In anything older, the select, the date pickers, the time picker's list and columns, the phone's country list, the dropdown and the tooltips can't open, because they're built on the browser's own popover. The rest still render, but aren't tested there.
  • On iPhone and iPad before iOS 18.3, tapping outside an open popover doesn't close it, because of a WebKit bug. Choosing an option or pressing its trigger again still closes it.

Content Security Policy

  • The components work under a strict policy such as default-src 'self', with no 'unsafe-inline' for scripts or styles. They render no inline scripts, event handlers or style attributes; sizes worked out on the server are classes.
  • Examples that call a component's JavaScript come with a script for your own JS file, not onclick.
  • The captcha's Turnstile, reCAPTCHA or hCaptcha option loads that provider's script, so allow its domain in script-src and frame-src.

Livewire

  • Livewire isn't needed, but the components work inside Livewire 3 and 4 components, checked in a browser. They keep what the person did through every render (an open modal or menu, a panel they opened, a running stopwatch, files they picked) while their content updates. Each one's page shows how under Usage.
  • Form controls bind with wire:model and need no name. They show the property's value after every render, a value set in PHP included, and its validation errors. The file upload sends files to Livewire's temporary uploads (WithFileUploads).
  • A component shows a toast with $this->dispatch('toast', type: 'success', message: 'Saved.') and opens or closes a modal with 'modal-open' and 'modal-close', each with an id. A flash before a redirect, wire:navigate included, still shows as a toast.
  • Components added to the page later, by a Livewire render, wire:navigate or HTML you fetch, set themselves up.
  • Where the browser owns the state, the server's props only set how it starts: an accordion's open, and a stopwatch or timer, which keeps running and reads its props once. A progress bar you drive with progress.set() goes in a wire:ignore. The captcha's Turnstile, reCAPTCHA and hCaptcha tokens aren't bound by wire:model; use those in a regular form.

When it isn't a fit

  • Projects on Tailwind CSS v3, Bootstrap or another CSS framework: the components are styled with Tailwind v4 classes and theme tokens.
  • Inertia apps whose pages are React or Vue: the components are Blade, so they can't render there.

Add components

Require the package, then add components by name. Each one brings the components, PHP helpers and validation rules it depends on. Before writing anything it checks package.json, and stops if Tailwind CSS is older than 4.1.

Terminal
composer require --dev larawellui/larawellui
php artisan larawell:add datepicker table
npm run build

Run it again whenever you like, for example after composer update; --installed updates every component you already have. Files you haven't edited get the new version, files you have edited are skipped and listed, and --force overwrites those too. It keeps track in larawellui.lock in your app's root, so commit that file.

Terminal
php artisan larawell:list              # everything you can add
php artisan larawell:add               # pick from a list
php artisan larawell:add --all
php artisan larawell:add select --dry-run  # show what would change

php artisan larawell:add --installed       # update everything you have
php artisan larawell:diff                  # what the skipped files would miss
php artisan larawell:diff select/index.blade.php

Where files go

What Where
Blade components, used as <x-widget.*> resources/views/components/widget/
JavaScript, imported from resources/js/app.js resources/js/widget/
Theme and base CSS, imported from resources/css/app.css resources/css/widget/
PHP helpers (FormField, ElementIds, and Countries for the phone) App\View\Widget
Validation rules (date rules, Captcha) App\Rules

To use other namespaces or paths, publish the config. Namespaces must sit under a PSR-4 root in your composer.json.

Terminal
php artisan vendor:publish --tag=larawellui-config

Theme

Components only use the colour names in resources/css/widget/theme.css. Change the values there and every component follows.

resources/css/widget/theme.css
@theme {
    --color-primary: #16a34a;       /* selected states, focus rings */
    --color-on-primary: #ffffff;
    --color-foreground: #101828;    /* main text */
    --color-field: #f5f6f8;         /* input backgrounds */
    --color-error: #d92d20;
    /* … */
}

Validating dates

The date pickers stop people choosing future dates using their own local today. Validate with the rules that come with them, not before_or_equal:today, which uses the server's UTC date and can reject a real local today.

app/Http/Requests/StoreBookingRequest.php
use App\Rules\MinimumAge;
use App\Rules\NotAfterToday;

return [
    'start_date' => ['required', new NotAfterToday],
    'date_of_birth' => ['required', new MinimumAge(18)],
];

Dates in any language

  • Dates are written the way your app's locale writes them: Sep 26, 2026 in en, 26 Sept 2026 in en_GB, 2026年9月26日 in ja. Month and weekday names, and digits, follow too. What's submitted is always Y-m-d.
  • The week starts on the locale's first day: Monday in most of the world, Sunday in the US, Saturday in parts of the Middle East. week-start overrides it, and locale sets a different locale for one picker.
  • For Arabic, Hebrew, Persian and Urdu, set dir="rtl" on <html>: the calendar, its arrows and the arrow keys mirror.
  • Formatting needs PHP's intl extension; without it, dates show as 2026-09-26.

The pickers' own text ("Choose a date", "Clear", "Previous month" and so on) is plain English in the component files, like every other component's. It's your copy, so change it there, or wrap it in __() if your app is multilingual.

For AI agents

Point your agent at /llms.txt. From there it can read any component's entry and either run larawell:add or write the files in itself. For an app built with Livewire, each entry's usage includes a Livewire example.

In your own app, php artisan larawell:mcp is an MCP server the agent can connect to. On top of the catalogue, it knows which components this app has, which installed files are out of date or edited, and it can install: a dry run first, which writes nothing until you agree, and never over files you edited.

Terminal, from your app's root
claude mcp add larawellui -- php artisan larawell:mcp

Claude Desktop, Windsurf, Zed, JetBrains and any other client: the command php, with the arguments /path/to/your-app/artisan larawell:mcp. The full path to artisan is what makes it work where a client may not start the server in your app's folder. If a desktop app can't find php, give it the full path too (which php).

/llms.txt
An index of every component, for agents that follow the llms.txt convention.
/r/index.json
Every component, with a link to its full entry.
/r/datepicker.json
One component's full entry: install command, props, examples and source.
php artisan larawell:list --json
The same catalogue offline, from the installed package.
php artisan larawell:mcp
An MCP server in your app: the catalogue, what this app has installed and edited, and installing with a dry run first.