centrex/tallui
TallUI — Laravel UI Components Library
Reusable Blade and Livewire UI components built on DaisyUI + Alpine.js. Includes layout helpers, interactive components, a full-featured DataTable with search/sort/export, and ApexCharts-powered chart components.
Contents
- Installation
- Configuration
- Blade Components
- Layout — Header, Page Header, Card, Stats
- Navigation — Sidebar, Breadcrumb, Menu, Tabs
- Overlays — Modal, Dialog, Drawer
- Feedback — Alert, Badge, Notification, Empty State, Loading, Progress
- Buttons & Icons
- Display — Accordion, Collapse, Carousel, Image Gallery, Image Library, Timeline, Steps, Avatar, Rating, Swap, Tags
- Form Components
- Livewire DataTable
- Charts
- Performance Blade Directives
- Testing
Installation
composer require centrex/tallui
php artisan vendor:publish --tag="tallui-config"
Requires: livewire/livewire ^3, blade-ui-kit/blade-heroicons (for icons).
Configuration
// config/tallui.php
'prefix' => 'tallui', // '' → <x-button />, 'tallui' → <x-tallui-button />
Run php artisan view:clear after changing the prefix.
Blade Components
All components pass through $attributes, so HTML attributes and DaisyUI/Tailwind classes merge cleanly.
Layout
Header
Sticky application navbar. Integrates with <x-tallui-sidebar> via sidebar-id.
<x-tallui-header
brand="MyApp"
brand-href="/"
brand-logo="/img/logo.svg"
sidebar-id="main-sidebar"
:sticky="true"
>
<x-slot:center>
<a href="/dashboard" class="btn btn-ghost btn-sm">Dashboard</a>
<a href="/reports" class="btn btn-ghost btn-sm">Reports</a>
</x-slot:center>
<x-slot:actions>
<x-tallui-theme-toggle />
<x-tallui-button icon="heroicon-o-bell" class="btn-ghost btn-circle" />
<div class="avatar"><div class="w-8 rounded-full"><img src="https://raw.githubusercontent.com/centrex/tallui/HEAD/avatar.jpg" /></div></div>
</x-slot:actions>
</x-tallui-header>
| Prop | Default | Description |
|---|---|---|
brand |
null |
Brand name text |
brand-href |
/ |
Brand link URL |
brand-logo |
null |
Logo image URL |
sticky |
true |
Stick to top of viewport |
shadow |
true |
Show bottom shadow |
height |
h-16 |
Tailwind height class |
sidebar-id |
'' |
Dispatches toggle-sidebar event when hamburger clicked |
Named slots: brandSlot, center, actions.
Sidebar
Slide-in navigation panel. Toggleable via Alpine events or the Header's hamburger button.
{{-- Layout wrapper --}}
<div class="flex min-h-screen">
<x-tallui-sidebar
id="main-sidebar"
position="left"
width="w-64"
:overlay="true"
:persistent="true"
header="Navigation"
>
<nav class="menu menu-md px-2">
<li><a href="/dashboard">Dashboard</a></li>
<li><a href="/users">Users</a></li>
</nav>
<x-slot:footerSlot>
<x-tallui-button label="Logout" icon="heroicon-o-arrow-right-on-rectangle" class="btn-ghost w-full" />
</x-slot:footerSlot>
</x-tallui-sidebar>
<main class="flex-1 p-6">...</main>
</div>
{{-- Open/close from anywhere --}}
<button @click="$dispatch('toggle-sidebar', 'main-sidebar')">Menu</button>
| Prop | Default | Description |
|---|---|---|
id |
sidebar |
Unique ID; used in Alpine events |
position |
left |
left or right |
width |
w-64 |
Tailwind width class |
overlay |
true |
Dark backdrop behind sidebar |
persistent |
false |
Always visible on collapse-breakpoint+ screens |
collapse-breakpoint |
lg |
Breakpoint where sidebar becomes always-visible |
header |
null |
Header text (use headerSlot for custom content) |
Named slots: headerSlot, footerSlot.
Page Header
<x-tallui-page-header title="Customers" subtitle="Manage your list" icon="o-users">
<x-slot:breadcrumbs>
<x-tallui-breadcrumb :links="[['label' => 'Home', 'href' => '/'], ['label' => 'Customers']]" />
</x-slot:breadcrumbs>
<x-slot:actions>
<x-tallui-button label="New" icon="heroicon-o-plus" class="btn-primary" wire:click="create" />
</x-slot:actions>
</x-tallui-page-header>
Card
<x-tallui-card title="Revenue" subtitle="Last 30 days" icon="o-chart-bar" :shadow="true" padding="normal">
<x-slot:actions>
<x-tallui-button icon="o-arrow-path" class="btn-ghost btn-sm" />
</x-slot:actions>
<p class="text-3xl font-bold">৳ 1,24,000</p>
<x-slot:footer>
<span class="text-sm text-base-content/50">Updated just now</span>
</x-slot:footer>
</x-tallui-card>
padding: none | compact | normal | loose.
Stats
<div class="stats shadow w-full">
<x-tallui-stat title="Revenue" value="৳1,24,000" icon="o-banknotes"
change="+12%" change-type="up" desc="vs last month" />
<x-tallui-stat title="Overdue" value="7" icon="o-exclamation-circle"
icon-color="text-error" change="-2" change-type="down" />
</div>
change-type: up | down | neutral.
Navigation
Breadcrumb
<x-tallui-breadcrumb :links="[
['label' => 'Home', 'href' => '/'],
['label' => 'Users', 'href' => '/users'],
['label' => 'John Doe'], {{-- no href = current page --}}
]" />
Menu
Link item:
<x-tallui-menu-item
label="Dashboard"
icon="home"
:link="route('dashboard')"
:active="request()->routeIs('dashboard')"
/>
Livewire link without navigate:
<x-tallui-menu-item
label="Users"
icon="users"
:link="route('users.index')"
:wire-navigate="false"
/>
Button action:
<x-tallui-menu-item
label="Logout"
icon="log-out"
as-button
wire:click="logout"
/>
Section title:
<x-tallui-menu-item separator section-title="Administration" />
<x-tallui-menu>
<x-tallui-menu-item
label="Dashboard"
icon="o-home"
:link="route('dashboard')"
:active="request()->routeIs('dashboard')"
/>
<x-tallui-menu-item separator section-title="Administration" />
<form method="POST" action="{{ route('logout') }}">
@csrf
<x-tallui-menu-item
label="Logout"
icon="o-arrow-right-on-rectangle"
as="button"
type="submit"
/>
</form>
<x-tallui-menu-item separator />
<x-tallui-menu-item
label="GitHub"
icon="s-code-bracket"
:link="'https://github.com/tallui/tallui'"
target="_blank"
/>
<x-tallui-menu-item>
<x-tallui-theme-toggle />
</x-tallui-menu-item>
</x-tallui-menu>
Tabs
<x-tallui-tab :tabs="['Overview', 'Analytics', 'Reports']" active="Overview" />
Overlays
Modal
Full-featured modal with backdrop, keyboard close, and named slots.
<x-tallui-modal id="confirm-delete" title="Delete user?" icon="heroicon-o-trash" icon-color="text-error">
<x-slot:trigger>
<x-tallui-button label="Delete" class="btn-error" />
</x-slot:trigger>
<p>Are you sure you want to delete this user? This action cannot be undone.</p>
<x-slot:footer>
<button class="btn" @click="$dispatch('close-modal', 'confirm-delete')">Cancel</button>
<button class="btn btn-error" wire:click="delete">Yes, delete</button>
</x-slot:footer>
</x-tallui-modal>
Open/close from anywhere: $dispatch('open-modal', 'confirm-delete').
| Prop | Default | Options |
|---|---|---|
id |
modal |
Unique ID |
size |
md |
sm | md | lg | xl | full |
closeable |
true |
Show × button + backdrop click to close |
Dialog
Lightweight centred confirmation dialog. Auto-selects icon and colour per type.
<x-tallui-dialog
id="delete-confirm"
type="error"
title="Delete this record?"
size="sm"
>
<x-slot:trigger>
<x-tallui-button label="Delete" class="btn-error btn-sm" />
</x-slot:trigger>
<p>This action is permanent and cannot be reversed.</p>
<x-slot:footer>
<button class="btn btn-ghost" @click="$dispatch('close-dialog', 'delete-confirm')">Cancel</button>
<button class="btn btn-error" wire:click="delete">Delete</button>
</x-slot:footer>
</x-tallui-dialog>
Open programmatically: $dispatch('open-dialog', 'delete-confirm').
type |
Icon | Colour |
|---|---|---|
info |
information-circle | text-info |
success |
check-circle | text-success |
warning |
exclamation-triangle | text-warning |
error |
x-circle | text-error |
confirm |
question-mark-circle | text-primary |
Drawer
<x-tallui-drawer id="cart" position="right" title="Shopping Cart">
<x-slot:trigger>
<x-tallui-button icon="heroicon-o-shopping-cart" class="btn-ghost" />
</x-slot:trigger>
{{-- Drawer body --}}
</x-tallui-drawer>
Dropdown
Floating action menu — positioned via Alpine and teleported to <body> so it isn't clipped by overflow-hidden ancestors. Full keyboard navigation (arrow keys, Escape).
<x-tallui-dropdown
position="bottom-end"
:items="[
['label' => 'Edit', 'icon' => 'o-pencil', 'url' => route('users.edit', $user)],
['separator' => true],
['label' => 'Delete', 'icon' => 'o-trash', 'color' => 'error', 'attributes' => ['wire:click' => 'delete(' . $user->id . ')', 'wire:confirm' => 'Are you sure?']],
]"
>
<x-slot:trigger>
<x-tallui-button icon="o-ellipsis-vertical" class="btn-ghost btn-sm" />
</x-slot:trigger>
</x-tallui-dropdown>
Each item: label, url?, icon?, color?, badge?, disabled?, separator?, sectionTitle?, attributes?. Full reference: docs/ui-components.md.
Feedback
Alerts
<x-tallui-alert type="warning" title="Action required" :dismissible="true">
Your subscription expires in 3 days.
</x-tallui-alert>
Types: info | success | warning | error. Icon auto-selected per type.
Badges
<x-tallui-badge type="success">Active</x-tallui-badge>
<x-tallui-badge type="warning" size="sm">Pending</x-tallui-badge>
<x-tallui-badge color="outline">Draft</x-tallui-badge>
Types/colors: success | error | warning | info | primary | secondary | accent | ghost | outline | neutral.
Sizes: xs | sm | md | lg.
Notifications
Place once in your layout. Reads session flash keys and listens for Livewire events:
{{-- layout.blade.php --}}
<x-tallui-notification position="top-right" :timeout="4000" />
// In any Livewire component
$this->dispatch('notify', type: 'success', message: 'Saved!');
$this->dispatch('notify', type: 'error', message: 'Something went wrong.');
Session keys auto-displayed: success, error, warning, info, message.
Positions: top-right | top-left | bottom-right | bottom-left | top-center.
Empty State
<x-tallui-empty-state
title="No invoices yet"
description="Create your first invoice to get started."
icon="heroicon-o-document-text"
size="md"
>
<x-tallui-button label="Create Invoice" icon="heroicon-o-plus" class="btn-primary" />
</x-tallui-empty-state>
Sizes: sm | md | lg.
Loading & Progress
<x-tallui-loading />
<x-tallui-progress value="72" color="primary" size="sm" />
Skeletons
Loading placeholders — use <x-tallui-skeleton> for a single bar/circle, or <x-tallui-card-skeleton> as a Livewire placeholder() view sized to match <x-tallui-card> so content doesn't shift when it swaps in.
<x-tallui-skeleton variant="circle" width="w-10" height="h-10" />
<x-tallui-skeleton variant="text" :lines="3" />
{{-- As a Livewire placeholder --}}
<x-tallui-card-skeleton title="Revenue" variant="stats" :rows="4" />
Buttons & Icons
{{-- Link with wire:navigate --}}
<x-tallui-button label="Edit" icon="o-pencil" :link="route('orders.edit', $order)" class="btn-ghost btn-sm" />
{{-- Loading spinner tied to wire:click --}}
<x-tallui-button wire:click="save" :spinner="save" label="Save" class="btn-primary" />
{{-- Responsive: hides label on mobile --}}
<x-tallui-button label="Delete" icon="o-trash" :responsive="true" class="btn-error" />
{{-- Tooltip --}}
<x-tallui-button icon="o-question-mark-circle" tooltip="Help" class="btn-ghost" />
{{-- Heroicon --}}
<x-tallui-icon name="heroicon-o-star" class="w-6 h-6 text-warning" />
Display
Accordion
<x-tallui-accordion name="faq" title="What is TallUI?" variant="arrow" :open="false">
TallUI is a Laravel UI component library built on DaisyUI and Alpine.js.
</x-tallui-accordion>
Variants: arrow | plus.
Collapse
Single collapsible panel with smooth Alpine.js transitions. Lighter than Accordion for standalone use.
<x-tallui-collapse title="Advanced settings" :bordered="true" variant="arrow">
<div class="flex flex-col gap-3 pt-2">
<x-tallui-toggle label="Enable debug mode" />
<x-tallui-toggle label="Show verbose logs" />
</div>
</x-tallui-collapse>
| Prop | Default | Description |
|---|---|---|
title |
'' |
Header text |
open |
false |
Expanded by default |
bordered |
false |
Border instead of filled background |
variant |
arrow |
arrow | plus | none |
Image Gallery
Read-only image grid with built-in lightbox:
<x-tallui-image-gallery
:images="[
['src' => '/img/1.jpg', 'alt' => 'Photo 1', 'caption' => 'Sunrise'],
['src' => '/img/2.jpg', 'alt' => 'Photo 2'],
]"
:columns="3"
:lightbox="true"
fit="cover"
height="h-48"
/>
Image Library
Selectable image picker with lightbox and form submission support. Double-click opens lightbox; single-click selects.
<x-tallui-image-library
:images="$mediaLibrary"
:multiple="true"
:selected="$selectedIds"
name="media_ids"
:columns="4"
fit="cover"
height="h-32"
/>
| Prop | Default | Description |
|---|---|---|
images |
[] |
Array of ['src', 'alt'?, 'caption'?, 'id'?] |
multiple |
false |
Allow multiple selections |
selected |
[] |
Pre-selected ids/srcs |
name |
null |
If set, renders hidden inputs for form posting |
selectable |
true |
Enable selection UI |
columns |
4 |
Grid column count (sm: 2, sm+: columns) |
Other Display Components
{{-- Carousel --}}
<x-tallui-carousel :slides="$slides" :autoplay="true" :interval="3000" />
{{-- Timeline --}}
<x-tallui-timeline :items="$events" />
{{-- Steps --}}
<x-tallui-steps :steps="['Cart', 'Shipping', 'Payment', 'Confirm']" :current="2" />
{{-- Rating --}}
<x-tallui-rating name="score" :value="3" :max="5" />
{{-- Avatar --}}
<x-tallui-avatar src="/img/user.jpg" size="md" />
{{-- Tags --}}
<x-tallui-tags :items="['Laravel', 'Livewire', 'Alpine']" />
{{-- Swap (toggle icon) --}}
<x-tallui-swap on-icon="heroicon-o-sun" off-icon="heroicon-o-moon" />
{{-- Theme Toggle --}}
<x-tallui-theme-toggle />
{{-- Popover --}}
<x-tallui-popover content="Helpful info">
<x-tallui-button icon="heroicon-o-information-circle" class="btn-ghost btn-xs" />
</x-tallui-popover>
{{-- Tooltip --}}
<x-tallui-tooltip text="Delete this record" position="top">
<x-tallui-button icon="o-trash" class="btn-ghost btn-sm" />
</x-tallui-tooltip>
{{-- Spotlight search --}}
<x-tallui-spotlight />
{{-- Kbd (keyboard shortcut hint) --}}
<x-tallui-kbd key="⌘" /> <x-tallui-kbd key="K" />
{{-- Divider --}}
<x-tallui-divider>OR</x-tallui-divider>
{{-- Chat bubble --}}
<x-tallui-chat-bubble position="start" name="Support" time="12:42">
Hi! How can we help you today?
</x-tallui-chat-bubble>
Form Components
Standard Inputs
<x-tallui-input name="email" label="Email" type="email" wire:model="email" :required="true" />
<x-tallui-textarea name="bio" label="Bio" :rows="4" wire:model="bio" />
<x-tallui-select name="role" label="Role" :options="$roles" wire:model="role" />
<x-tallui-checkbox name="agree" label="I agree to the terms" wire:model="agree" />
<x-tallui-radio name="gender" label="Male" value="male" wire:model="gender" />
<x-tallui-toggle name="active" label="Active" wire:model="active" />
<x-tallui-date-picker name="birthday" label="Birthday" wire:model="birthday" />
<x-tallui-form-group label="Notes" error="Required" helper="Max 500 chars">
<x-tallui-textarea name="notes" />
</x-tallui-form-group>
Select
<x-tallui-select /> supports three common modes:
- Standard select with automatically sorted labels
- Searchable select with client-side filtering for static options
- Async searchable select backed by a model registry or custom endpoint
{{-- Standard select: options are sorted by label by default --}}
<x-tallui-select
name="role"
label="Role"
:options="$roles"
wire:model="role"
/>
{{-- Local searchable select for static options --}}
<x-tallui-select
name="warehouse_id"
label="Warehouse"
:options="$warehouses"
searchable
wire:model="warehouse_id"
/>
{{-- Async searchable select using the TallUI search registry --}}
<x-tallui-select
name="customer_id"
label="Customer"
searchable
search-name="customer"
wire:model="customer_id"
/>
If searchable is enabled without search-name or search-url, the component filters the provided options in the browser. If searchable is combined with search-name or search-url, it performs async lookups instead.
You can also enqueue a source definition directly on the component. TallUI stores that definition temporarily in cache and the async endpoint reads it back using a signed token-style lookup key.
<x-tallui-select
name="customer_id"
label="Customer"
searchable
:search-source="[
'model' => App\Models\Customer::class,
'label' => 'name',
'sublabel' => 'email',
'value' => 'id',
'search_columns' => ['name', 'email', 'phone'],
'order_by' => 'name',
'order_direction' => 'asc',
'limit' => 25,
]"
wire:model="customer_id"
/>
| Prop | Default | Description |
|---|---|---|
options |
[] |
Flat associative array or [{value, label, sublabel?}] list |
searchable |
false |
Enables search mode |
search-name |
null |
Registry key for the built-in async search route |
search-url |
null |
Fully custom async search endpoint |
search-source |
[] |
Inline async source definition stored temporarily in cache |
sort |
true |
Sort static options by label |
placeholder |
null |
Placeholder/empty option label |
required |
false |
Marks the field as required |
disabled |
false |
Disables the field |
Async Select Configuration
Register searchable models in config/tallui.php:
'searchable_models' => [
'customer' => [
'model' => App\Models\Customer::class,
'label' => 'name',
'sublabel' => 'email',
'value' => 'id',
'search_columns' => ['name', 'email', 'phone'],
'order_by' => 'name',
'order_direction' => 'asc',
'limit' => 25,
],
],
search_source_ttl controls how long inline search-source definitions stay available in cache.
<x-tallui-select
name="customer_id"
label="Customer"
searchable
search-name="customer"
wire:model="customer_id"
/>
| Config Key | Description |
|---|---|
model |
Eloquent model class used for lookups |
label |
Column used for the displayed text |
sublabel |
Optional secondary column shown below the main label |
value |
Column returned as the selected value |
search_columns |
Columns searched by the async endpoint |
order_by |
Default ordering column |
order_direction |
asc or desc |
limit |
Maximum results returned per query |
Range Slider
<x-tallui-range
name="budget"
label="Budget"
:min="0"
:max="10000"
:step="100"
:value="5000"
color="primary"
:show-value="true"
:show-steps="true"
/>
Rich Text Editor
Content-editable rich text editor with formatting toolbar (bold, italic, underline, lists, headings). Output stored in a hidden <input> for form submission.
<x-tallui-text-editor
name="content"
label="Article body"
:value="old('content', $post->content)"
placeholder="Start writing…"
:rows="10"
:required="true"
/>
Choices (Multi-Select)
Searchable multi-select with tag badges. Outputs hidden inputs for form submission.
<x-tallui-choices
name="tags[]"
label="Tags"
:options="['laravel' => 'Laravel', 'vue' => 'Vue.js', 'alpine' => 'Alpine.js']"
:selected="['laravel']"
:multiple="true"
:searchable="true"
placeholder="Select tags…"
/>
Options can also be a list of ['value' => '...', 'label' => '...'] arrays.
| Prop | Default | Description |
|---|---|---|
options |
[] |
Flat associative array or [{value, label}] list |
selected |
[] |
Pre-selected values |
multiple |
true |
Allow multiple selections |
searchable |
true |
Show search input in dropdown |
File Upload (Drag & Drop)
Drag-and-drop zone with client-side file size validation and image preview thumbnails.
<x-tallui-file-upload
name="attachments"
label="Attachments"
wire:model="attachments"
:multiple="true"
accept="image/*,.pdf"
:max-size-mb="5"
:preview="true"
upload-text="Drop files here or click to upload"
/>
| Prop | Default | Description |
|---|---|---|
multiple |
false |
Accept multiple files |
accept |
null |
MIME/extension filter (e.g. image/*,.pdf) |
max-size-mb |
10 |
Client-side size limit per file |
preview |
true |
Show image thumbnails and file cards below zone |
For Livewire uploads, pass wire:model directly to the component; it is forwarded to the underlying file input.
File Input (Simple)
<x-tallui-file
name="avatar"
label="Profile Photo"
accept="image/*"
:required="true"
/>
PIN / OTP Input
Auto-advancing digit inputs with paste support, backspace navigation, and masked mode.
<x-tallui-pin
name="otp"
label="Verification code"
:length="6"
:masked="false"
:numeric="true"
size="md"
helper="Check your email for the 6-digit code."
/>
| Prop | Default | Description |
|---|---|---|
length |
6 |
Number of digit inputs |
masked |
false |
password-type inputs (dots) |
numeric |
true |
Only allow digits; set false for alphanumeric OTPs |
size |
md |
sm | md | lg |
Behaviour: typing auto-advances to the next cell, Backspace moves back, paste distributes across all cells, a visual separator is inserted at the midpoint for 6+ digit codes.
Livewire DataTable
Extend DataTable, override columns() and query():
use Centrex\TallUi\DataTable\Column;
use Centrex\TallUi\Livewire\DataTable;
use Illuminate\Database\Eloquent\Builder;
class CustomerTable extends DataTable
{
public string $defaultSortBy = 'name';
public function columns(): array
{
return [
Column::make('Name', 'name')->searchable()->sortable(),
Column::make('Email', 'email')->searchable(),
Column::make('Balance', 'outstanding_balance')->sortable(),
Column::make('Status', 'status')->badge('neutral', ['active' => 'success', 'inactive' => 'error']),
Column::make('Actions')->actions([...]),
];
}
public function query(): Builder
{
return Customer::query();
}
}
<livewire:tallui-data-table />
Features: URL-synced search/sort/page (#[Url]), sortable-column allowlisting, default sort support, per-page selector with normalization, row selection, CSV export (chunked + UTF-8 BOM), optional result caching via $cacheTtl, and relation-aware search for searchable columns such as customer.name.
DataTable Notes
- Only columns marked with
->sortable()can be used for sorting. - Set
$defaultSortByand$defaultSortDirectionto define the initial ordering. - Searchable relation columns such as
customer.nameare supported in the global search. - Invalid
perPagevalues fall back to the first configured per-page option. Filter::select()andFilter::boolean()apply real query constraints instead of acting as UI-only filters.
Responsive DataTable
Automatically switches between a desktop table and a mobile card stack at the md breakpoint. Change the breakpoint by overriding $mobileBreakpoint:
class CustomerTable extends DataTable
{
public string $mobileBreakpoint = 'lg'; // cards below 1024 px
// public string $mobileBreakpoint = ''; // disable card stack
}
Hide columns at smaller widths:
Column::make('Name', 'name')->searchable()->sortable(), // always visible
Column::make('Email', 'email')->hideOnMobile(), // hidden below md
Column::make('Phone', 'phone')->visibleFrom('lg'), // hidden below lg
Column::make('Tax ID', 'tax_id')->visibleFrom('xl'), // hidden below xl
Charts
All charts are Livewire components powered by ApexCharts (loaded via CDN, configurable in config/tallui.php).
Standard Charts
<livewire:tallui-line-chart :series="$series" :categories="$months" title="Revenue" />
<livewire:tallui-bar-chart :series="$series" :categories="$months" :horizontal="false" />
<livewire:tallui-area-chart :series="$series" :categories="$months" :smooth="true" />
<livewire:tallui-pie-chart :series="$values" :categories="$labels" />
<livewire:tallui-pie-chart :series="$values" :categories="$labels" :donut="true" />
Common props (all charts): title, subtitle, height (px, default 350), theme (light|dark), poll (ms, 0 = disabled), data-provider (FQCN of ChartDataProvider).
Mixed (Combo) Chart
Combines bar, line, and area series in one canvas. Each series must include a type key.
<livewire:tallui-mixed-chart :series="$series" :categories="$months" title="Revenue vs Trend" />
$series = [
['name' => 'Revenue', 'type' => 'bar', 'data' => [50000, 70000, 80000, 60000]],
['name' => 'Trend', 'type' => 'line', 'data' => [55000, 65000, 75000, 70000]],
['name' => 'Forecast', 'type' => 'area', 'data' => [48000, 68000, 78000, 72000]],
];
$months = ['Jan', 'Feb', 'Mar', 'Apr'];
Treemap Chart
Hierarchical tile visualisation. Series uses {x, y} data pairs; multiple series = multiple colour groups.
<livewire:tallui-treemap-chart :series="$series" title="Product Sales" />
$series = [
[
'name' => 'Electronics',
'data' => [
['x' => 'Phones', 'y' => 90],
['x' => 'Laptops', 'y' => 75],
['x' => 'Tablets', 'y' => 40],
],
],
[
'name' => 'Clothing',
'data' => [
['x' => 'Shirts', 'y' => 55],
['x' => 'Shoes', 'y' => 38],
],
],
];
Props: :distributed="true" (each tile its own colour), :enable-shades="true".
Radial Bar Chart
Gauge-style circular arcs. Each arc represents one percentage value. Shows averaged total in the centre.
<livewire:tallui-radial-bar-chart
:series="[75, 55, 90]"
:categories="['CPU', 'Memory', 'Disk']"
title="Server Usage"
start-angle="-135"
end-angle="135"
/>
| Prop | Default | Description |
|---|---|---|
series |
[] |
Flat array of percentages (0–100) |
categories |
[] |
Arc labels, one per value |
start-angle |
-135 |
Start angle in degrees |
end-angle |
135 |
End angle in degrees |
hollow |
true |
Donut-style hollow centre |
track |
'' |
Custom track background colour |
Radar Chart
Spider/web chart for multi-dimensional comparison. Supports multiple series on the same grid.
<livewire:tallui-radar-chart
:series="$series"
:categories="['Speed', 'Power', 'Range', 'Efficiency', 'Comfort', 'Safety']"
title="Product Comparison"
/>
$series = [
['name' => 'Model A', 'data' => [80, 50, 30, 40, 100, 20]],
['name' => 'Model B', 'data' => [60, 85, 70, 55, 75, 60]],
];
Polar Area Chart
Proportional sectors that also extend outward by value — like a pie chart with a size dimension. Series is a flat array; categories are the sector labels.
<livewire:tallui-polar-area-chart
:series="[42, 18, 35, 27, 14]"
:categories="['North', 'South', 'East', 'West', 'Central']"
title="Regional Distribution"
/>
Range Area Chart
Shaded band between a low and a high value per x-point. Ideal for confidence intervals, temperature ranges, and min/max visualisations.
<livewire:tallui-range-area-chart :series="$series" title="Temperature Range" />
$series = [
[
'name' => 'Temperature',
'data' => [
['x' => 'Jan', 'y' => [2, 12]],
['x' => 'Feb', 'y' => [3, 15]],
['x' => 'Mar', 'y' => [8, 22]],
['x' => 'Apr', 'y' => [13, 28]],
],
],
];
Props: :smooth="true" — smooth or straight stroke.
Data Providers
For charts fed from a service or cache, implement ChartDataProvider:
use Centrex\TallUi\Contracts\ChartDataProvider;
class RevenueDataProvider implements ChartDataProvider
{
public function getData(): array
{
return [
'series' => [['name' => 'Revenue', 'data' => [50, 70, 80]]],
'categories' => ['Jan', 'Feb', 'Mar'],
];
}
}
<livewire:tallui-line-chart data-provider="App\Charts\RevenueDataProvider" />
Performance Blade Directives
Registered automatically by the service provider.
@pushonce / @endpushonce
Push a block to a named stack exactly once per request — prevents duplicate <script> or <link> tags when a component renders multiple times on one page.
@pushonce('scripts', 'my-lib')
<script src="/vendor/my-lib.js"></script>
@endpushonce
The second argument is the deduplication key. Omit it to use the stack name as the key.
@memoize / @endmemoize
Render a Blade block once per request, then replay the cached HTML on every subsequent call. Use inside loops for expensive sub-views (icon sets, nav links, etc.).
@foreach($rows as $row)
@memoize('shared-icon-set')
<x-tallui-icon name="heroicon-o-check" class="w-4 h-4" />
@endmemoize
@endforeach
The block is rendered on the first encounter; all subsequent calls output the same cached string instantly.
@lazy / @endlazy
Defer Alpine.js initialisation until the wrapped content scrolls near the viewport (uses Alpine x-intersect). Reduces the JS work on initial page load for below-the-fold components.
@lazy
<livewire:tallui-bar-chart :series="$series" />
@endlazy
Requires Alpine's x-intersect plugin (bundled with Livewire 3 / Alpine 3.x).
@styleonce / @endstyleonce
Push a <style> block to the styles stack at most once, regardless of how many times the component renders.
@styleonce('pin-input')
<style>
.pin-cell { letter-spacing: 0.5em; }
</style>
@endstyleonce
@scriptonce / @endscriptonce
Push a <script> block to the scripts stack at most once.
@scriptonce('my-init')
<script>
window.myLib = { version: '1.0' };
</script>
@endscriptonce
Testing
composer test # full suite: rector dry-run, pint check, phpstan, pest
composer test:unit # pest tests only
composer test:types # phpstan static analysis
composer lint # apply pint formatting
Changelog
Please see CHANGELOG for more information on what has changed recently.
Credits
License
The MIT License (MIT). Please see License File for more information.
Related Packages
Shopping cart for Laravel with session, cookie, and database storage, Livewire c...