## Choose your install path

Layup is a Filament form field first; the bundled Pages resource is a turnkey example built on top of it. Pick the path that fits how you plan to use it.

**Path A -- Use Layup as a field on your own model.** Skip the Pages resource. You bring the model, the JSON column, and your own Filament resource. Layup gives you the editor canvas. Continue at [Field-only installation](field-only-installation.md), then read [Embedding the field](embedding-the-field.md).

**Path B -- Use the bundled Pages resource.** A full page CMS with nested pages, scheduled publishing, SEO meta, and frontend routes -- ready to use after `php artisan layup:install`. Continue below.

Either way, the field looks like this:

```php
use Crumbls\Layup\Forms\Components\LayupBuilder;

LayupBuilder::make('content')->columnSpanFull()
```

## Requirements

- PHP 8.3+
- Laravel 12 or 13
- Filament 5

`livewire/livewire` is suggested only when you build widgets that extend `BaseLivewireWidget`.

## Prerequisites

Layup requires a working Filament installation. If you have not set up Filament yet:

```bash
composer require filament/filament
php artisan filament:install --panels
```

See the [Filament installation docs](https://filamentphp.com/docs/panels/installation) for details.

## Install via Composer

```bash
composer require crumbls/layup
```

## Register the plugin

Add `LayupPlugin` to your Filament panel provider (e.g., `app/Providers/Filament/AdminPanelProvider.php`):

```php
use Crumbls\Layup\LayupPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugins([
            LayupPlugin::make(),
        ]);
}
```

Registering the plugin makes the `LayupBuilder` field available in any Filament form and (unless disabled in config) registers the Pages resource in your sidebar.

## Quick install (recommended)

Run the install command to handle everything in one step:

```bash
php artisan layup:install
```

This:

- Publishes the config file
- Runs database migrations (creates `layup_pages` and `layup_page_revisions`)
- Creates a storage symlink (`storage:link`) so uploaded images are web-accessible
- Creates the frontend layout component if it does not exist
- Publishes Filament assets (CSS)
- Generates the Tailwind safelist
- Checks your setup and warns about any issues

After running install, skip to [Tailwind safelist setup](#tailwind-safelist-setup).

## Manual installation

If you prefer to run each step yourself:

```bash
php artisan vendor:publish --tag=layup-config
php artisan migrate
php artisan storage:link
php artisan filament:assets
php artisan layup:safelist
```

This creates two tables: `layup_pages` (configurable name) and `layup_page_revisions`.

## Tailwind safelist setup

Layup dynamically generates CSS classes for column widths, spacing, and user-defined styles. Tailwind needs to know about these classes.

### Tailwind v4

Add to your CSS entry point:

```css
@source "../../storage/layup-safelist.txt";
```

### Tailwind v3

Add to your `tailwind.config.js`:

```js
module.exports = {
    content: [
        // ...
        './storage/layup-safelist.txt',
    ],
};
```

Generate the safelist file (if you did not use `layup:install`):

```bash
php artisan layup:safelist
```

When `safelist.auto_sync` is enabled in config (the default), the safelist regenerates automatically every time a page is saved.

### Build pipeline integration

Add the safelist command to your build script so it always runs before Tailwind compiles:

```json
{
    "scripts": {
        "build": "php artisan layup:safelist && vite build"
    }
}
```

### Rebuild frontend assets

```bash
npm run build
```

Without the safelist and rebuild steps, the page builder will work in the admin panel but frontend pages will have broken or missing styling.

## Publish assets (optional)

| Tag | Command | Description |
|-----|---------|-------------|
| `layup-config` | `php artisan vendor:publish --tag=layup-config` | Config file |
| `layup-views` | `php artisan vendor:publish --tag=layup-views` | Blade templates |
| `layup-routes` | `php artisan vendor:publish --tag=layup-routes` | Route file |
| `layup-scripts` | `php artisan vendor:publish --tag=layup-scripts` | Alpine.js components |
| `layup-templates` | `php artisan vendor:publish --tag=layup-templates` | Page templates |
| `layup-translations` | `php artisan vendor:publish --tag=layup-translations` | Language files |
| `layup-stubs` | `php artisan vendor:publish --tag=layup-stubs` | Widget scaffold stubs |

## Verify installation

Run the health check command to confirm everything is set up correctly:

```bash
php artisan layup:doctor
```

Then visit `/admin/pages` (or your panel path) and create a new page. You should see the visual builder with rows, columns, and the widget picker.

## Troubleshooting

`php artisan layup:doctor` runs a battery of checks and prints `passed / warning / failure` counts. Below are the failures you are most likely to hit and how to fix each.

### "LayupPlugin is not registered in any Filament panel"

The `LayupPlugin::make()` call is missing from your panel provider. Add it under `->plugins([...])` (see [Register the plugin](#register-the-plugin)). After editing, clear the config cache: `php artisan config:clear`.

### "Table 'layup_pages' does not exist"

Migrations have not run. Run:

```bash
php artisan migrate
```

If you are intentionally on the field-only path and do not want these tables, see [Field-only installation](field-only-installation.md) -- the doctor warning is expected there.

### "Storage symlink missing"

Uploaded images will not be web-accessible. Run:

```bash
php artisan storage:link
```

### "Layout component [app] not found"

Layup expects a Blade component matching `frontend.layout` (default: `app`) to exist at `resources/views/components/app.blade.php`. Either:

- Run `php artisan layup:install` -- it copies a starter layout component if one is missing, or
- Create the component manually, or
- Set `frontend.layout` in `config/layup.php` to point to your existing layout component (e.g., `'layouts.app'` for `resources/views/components/layouts/app.blade.php`).

### "Layout does not include @layupScripts"

Interactive widgets (accordion, tabs, countdown, modals, slider, etc.) will not function without this directive. Add it to your frontend layout, typically just before the closing `</body>` tag:

```blade
@layupScripts
</body>
```

### "Safelist file does not exist" / Frontend pages render without styling

Tailwind cannot see Layup's dynamically-generated classes. Verify three things in order:

1. The safelist file exists -- run `php artisan layup:safelist` to generate it.
2. Your Tailwind config or CSS entry point references the safelist path (see [Tailwind safelist setup](#tailwind-safelist-setup)).
3. You ran `npm run build` (or your equivalent) **after** adding the safelist reference.

### "Upload disk 'public' is not configured"

Your `config/filesystems.php` does not have a disk matching `uploads.disk`. Either configure the disk or set `uploads.disk` in `config/layup.php` to a disk you do have configured.

If a check is not listed here, run `php artisan layup:doctor` and read the message inline -- each failure includes a one-line fix hint.

## Upgrades

See [CHANGELOG.md](https://github.com/Crumbls/layup/blob/main/CHANGELOG.md) for release notes, breaking changes, and version-by-version migration guidance.

---

## Documentation

- [Introduction](https://documentation.crumbls.com/layup/-/index.md)
- [Introduction](https://documentation.crumbls.com/layup/-/introduction.md)
- [v1](https://documentation.crumbls.com/layup/-/_index.md)
- [Contributing to Layup](https://documentation.crumbls.com/layup/-/CONTRIBUTING.md)
- [Usage](https://documentation.crumbls.com/layup/-/usage.md)
- [Configuration](https://documentation.crumbls.com/layup/-/configuration.md)
- [Examples](https://documentation.crumbls.com/layup/-/examples.md)
- [Grid System](https://documentation.crumbls.com/layup/-/grid-system.md)
- [Field-only Installation](https://documentation.crumbls.com/layup/-/field-only-installation.md)
- [Embedding the Field](https://documentation.crumbls.com/layup/-/embedding-the-field.md)
- [Widgets](https://documentation.crumbls.com/layup/-/widgets/_index.md)
- [Getting Started](https://documentation.crumbls.com/layup/-/getting-started/_index.md)
- [Customization](https://documentation.crumbls.com/layup/-/customization/_index.md)
- [API Reference](https://documentation.crumbls.com/layup/-/api-reference/_index.md)
- [Upgrade guide](https://documentation.crumbls.com/layup/-/upgrade-guide.md)
- [Troubleshooting](https://documentation.crumbls.com/layup/-/troubleshooting.md)
- [Changelog](https://documentation.crumbls.com/layup/-/CHANGELOG.md)
- [🔐 Security Policy](https://documentation.crumbls.com/layup/-/SECURITY.md)
- [The MIT License (MIT)](https://documentation.crumbls.com/layup/-/LICENSE.md)
- [Layup Widget Development Guide for AI Agents](https://documentation.crumbls.com/layup/-/AGENTS.md)
- [Layup — Sprint TODO (overnight Feb 23-24)](https://documentation.crumbls.com/layup/-/TODO.md)
- [Vision](https://documentation.crumbls.com/layup/-/VISION.md)
- [Content Widgets](https://documentation.crumbls.com/layup/-/widgets/content.md)
- [Media Widgets](https://documentation.crumbls.com/layup/-/widgets/media.md)
- [Interactive Widgets](https://documentation.crumbls.com/layup/-/widgets/interactive.md)
- [Layout Widgets](https://documentation.crumbls.com/layup/-/widgets/layout.md)
- [Advanced Widgets](https://documentation.crumbls.com/layup/-/widgets/advanced.md)
- [Creating Pages](https://documentation.crumbls.com/layup/-/getting-started/creating-pages.md)
- [Adding Widgets](https://documentation.crumbls.com/layup/-/getting-started/adding-widgets.md)
- [Saving and Publishing](https://documentation.crumbls.com/layup/-/getting-started/publishing.md)
- [Disable the Pages Resource](https://documentation.crumbls.com/layup/-/customization/disable-pages-resource.md)
- [Rendering Content](https://documentation.crumbls.com/layup/-/customization/rendering-content.md)
- [Frontend Rendering](https://documentation.crumbls.com/layup/-/customization/frontend-rendering.md)
- [Creating Custom Widgets](https://documentation.crumbls.com/layup/-/customization/custom-widgets.md)
- [Extending Existing Widgets](https://documentation.crumbls.com/layup/-/customization/extending-widgets.md)
- [Livewire-rendered widgets](https://documentation.crumbls.com/layup/-/customization/livewire-widgets.md)
- [Filament Plugin API](https://documentation.crumbls.com/layup/-/customization/filament-plugin-api.md)
- [Theme System](https://documentation.crumbls.com/layup/-/customization/theme-system.md)
- [Tailwind Safelist](https://documentation.crumbls.com/layup/-/customization/tailwind-safelist.md)
- [SEO Meta](https://documentation.crumbls.com/layup/-/customization/seo-meta.md)
- [Page Templates](https://documentation.crumbls.com/layup/-/customization/page-templates.md)
- [Revision History](https://documentation.crumbls.com/layup/-/customization/revision-history.md)
- [Swapping the Page Model](https://documentation.crumbls.com/layup/-/customization/swapping-the-page-model.md)
- [Testing](https://documentation.crumbls.com/layup/-/customization/testing.md)
- [Widget Contract](https://documentation.crumbls.com/layup/-/api-reference/widget-contract.md)
- [Models](https://documentation.crumbls.com/layup/-/api-reference/models.md)
- [Service Provider](https://documentation.crumbls.com/layup/-/api-reference/service-provider.md)
- [Support Classes](https://documentation.crumbls.com/layup/-/api-reference/support-classes.md)
- [Commands](https://documentation.crumbls.com/layup/-/api-reference/commands.md)
