Instruction file imported from blhk0532/nds-livewire (
.cursor/rules/laravel-boost.mdc). Copyright stays with the author.
title: Build a standalone plugin
Preface
Please read the docs on panel plugin development and the getting started guide before continuing.
Introduction
In this walkthrough, we'll build a simple plugin that adds a new form component that can be used in forms. This also means it will be available to users in their panels.
You can find the final code for this plugin at https://github.com/awcodes/headings.
Step 1: Create the plugin
First, we'll create the plugin using the steps outlined in the getting started guide.
Step 2: Clean up
Next, we'll clean up the plugin to remove the boilerplate code we don't need. This will seem like a lot, but since this is a simple plugin, we can remove a lot of the boilerplate code.
Remove the following directories and files:
binconfigdatabasesrc/Commandssrc/Facadesstubs
Now we can clean up our composer.json file to remove unneeded options.
"autoload": {
"psr-4": {
// We can remove the database factories
"Awcodes\Headings\Database\Factories\": "database/factories/"
}
},
"extra": {
"laravel": {
// We can remove the facade
"aliases": {
"Headings": "Awcodes\Headings\Facades\ClockWidget"
}
}
},
Normally, Filament recommends that users style their plugins with a custom filament theme, but for the sake of example let's provide our own stylesheet that can be loaded asynchronously using the new x-load features in Filament v3. So, let's update our package.json file to include cssnano, postcss, postcss-cli and postcss-nesting to build our stylesheet.
{
"private": true,
"scripts": {
"build": "postcss resources/css/index.css -o resources/dist/headings.css"
},
"devDependencies": {
"cssnano": "^6.0.1",
"postcss": "^8.4.27",
"postcss-cli": "^10.1.0",
"postcss-nesting": "^13.0.0"
}
}
Then we need to install our dependencies.
npm install
We will also need to update our postcss.config.js file to configure postcss.
module.exports = {
plugins: [
require('postcss-nesting')(),
require('cssnano')({
preset: 'default',
}),
],
};
You may also remove the testing directories and files, but we'll leave them in for now, although we won't be using them for this example, and we highly recommend that you write tests for your plugins.
Step 3: Setting up the provider
Now that we have our plugin cleaned up, we can start adding our code. The boilerplate in the src/HeadingsServiceProvider.php file has a lot going on so, let's delete everything and start from scratch.
We need to be able to register our stylesheet with the Filament Asset Manager so that we can load it on demand in our Blade view. To do this, we'll need to add the following to the packageBooted method in our service provider.
Note the loadedOnRequest() method. This is important, because it tells Filament to only load the stylesheet when it's needed.
namespace Awcodes\Headings;
use Filament\Support\Assets\Css;
use Filament\Support\Facades\FilamentAsset;
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\PackageServiceProvider;
class HeadingsServiceProvider extends PackageServiceProvider
{
public static string $name = 'headings';
public function configurePackage(Package $package): void
{
$package->name(static::$name)
->hasViews();
}
public function packageBooted(): void
{
FilamentAsset::register([
Css::make('headings', __DIR__ . '/../resources/dist/headings.css')->loadedOnRequest(),
], 'awcodes/headings');
}
}
Step 4: Creating our component
Next, we'll need to create our component. Create a new file at src/Heading.php and add the following code.
namespace Awcodes\Headings;
use Closure;
use Filament\Schemas\Components\Component;
use Filament\Support\Colors\Color;
use Filament\Support\Concerns\HasColor;
class Heading extends Component
{
use HasColor;
protected string | int $level = 2;
protected string | Closure $content = '';
protected string $view = 'headings::heading';
final public function __construct(string | int $level)
{
$this->level($level);
}
public static function make(string | int $level): static
{
return app(static::class, ['level' => $level]);
}
public function content(string | Closure $content): static
{
$this->content = $content;
return $this;
}
public function level(string | int $level): static
{
$this->level = $level;
return $this;
}
public function getColor(): array
{
return $this->evaluate($this->color) ?? Color::Amber;
}
public function getContent(): string
{
return $this->evaluate($this->content);
}
public function getLevel(): string
{
return is_int($this->level) ? 'h' . $this->level : $this->level;
}
}
Step 5: Rendering our component
Next, we'll need to create the view for our component. Create a new file at resources/views/heading.blade.php and add the following code.
We are using x-load to asynchronously load stylesheet, so it's only loaded when necessary. You can learn more about this in the Core Concepts section of the docs.
@php
$level = $getLevel();
$color = $getColor();
@endphp
<{{ $level }}
x-data
x-load-css="[@js(\Filament\Support\Facades\FilamentAsset::getStyleHref('headings', package: 'awcodes/headings'))]"
{{
$attributes
->class([
'headings-component',
match ($color) {
'gray' => 'text-gray-600 dark:text-gray-400',
default => 'text-custom-500',
},
])
->style([
\Filament\Support\get_color_css_variables($color, [500]) => $color !== 'gray',
])
}}
>
{{ $getContent() }}
</{{ $level }}>
Step 6: Adding some styles
Next, let's provide some custom styling for our field. We'll add the following to resources/css/index.css. And run npm run build to compile our CSS.
.headings-component {
&:is(h1, h2, h3, h4, h5, h6) {
font-weight: 700;
letter-spacing: -.025em;
line-height: 1.1;
}
&h1 {
font-size: 2rem;
}
&h2 {
font-size: 1.75rem;
}
&h3 {
font-size: 1.5rem;
}
&h4 {
font-size: 1.25rem;
}
&h5,
&h6 {
font-size: 1rem;
}
}
Then we need to build our stylesheet.
npm run build
Step 7: Update your README
You'll want to update your README.md file to include instructions on how to install your plugin and any other information you want to share with users, Like how to use it in their projects. For example:
use Awcodes\Headings\Heading;
Heading::make(2)
->content('Product Information')
->color(Color::Lime),
And, that's it, our users can now install our plugin and use it in their projects.
=== .ai/03-building-a-panel-plugin rules ===
title: Build a panel plugin
import Aside from "@components/Aside.astro"
Preface
Please read the docs on panel plugin development and the getting started guide before continuing.
Introduction
In this walkthrough, we'll build a simple plugin that adds a new form field that can be used in forms. This also means it will be available to users in their panels.
You can find the final code for this plugin at https://github.com/awcodes/clock-widget.
Step 1: Create the plugin
First, we'll create the plugin using the steps outlined in the getting started guide.
Step 2: Clean up
Next, we'll clean up the plugin to remove the boilerplate code we don't need. This will seem like a lot, but since this is a simple plugin, we can remove a lot of the boilerplate code.
Remove the following directories and files:
configdatabasesrc/Commandssrc/Facadesstubs
Since our plugin doesn't have any settings or additional methods needed for functionality, we can also remove the ClockWidgetPlugin.php file.
ClockWidgetPlugin.php
Since Filament recommends that users style their plugins with a custom filament theme, we'll remove the files needed for using CSS in the plugin. This is optional, and you can still use CSS if you want, but it is not recommended.
resources/csspostcss.config.js
Now we can clean up our composer.json file to remove unneeded options.
"autoload": {
"psr-4": {
// We can remove the database factories
"Awcodes\ClockWidget\Database\Factories\": "database/factories/"
}
},
"extra": {
"laravel": {
// We can remove the facade
"aliases": {
"ClockWidget": "Awcodes\ClockWidget\Facades\ClockWidget"
}
}
},
The last step is to update the package.json file to remove unneeded options. Replace the contents of package.json with the following.
{
"private": true,
"type": "module",
"scripts": {
"dev": "node bin/build.js --dev",
"build": "node bin/build.js"
},
"devDependencies": {
"esbuild": "^0.17.19"
}
}
Then we need to install our dependencies.
npm install
You may also remove the Testing directories and files, but we'll leave them in for now, although we won't be using them for this example, and we highly recommend that you write tests for your plugins.
Step 3: Setting up the provider
Now that we have our plugin cleaned up, we can start adding our code. The boilerplate in the src/ClockWidgetServiceProvider.php file has a lot going on so, let's delete everything and start from scratch.
We need to be able to register our Widget with the panel and load our Alpine component when the widget is used. To do this, we'll need to add the following to the packageBooted method in our service provider. This will register our widget component with Livewire and our Alpine component with the Filament Asset Manager.
use Filament\Support\Assets\AlpineComponent;
use Filament\Support\Facades\FilamentAsset;
use Livewire\Livewire;
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\PackageServiceProvider;
class ClockWidgetServiceProvider extends PackageServiceProvider
{
public static string $name = 'clock-widget';
public function configurePackage(Package $package): void
{
$package->name(static::$name)
->hasViews()
->hasTranslations();
}
public function packageBooted(): void
{
Livewire::component('clock-widget', ClockWidget::class);
// Asset Registration
FilamentAsset::register(
assets:[
AlpineComponent::make('clock-widget', __DIR__ . '/../resources/dist/clock-widget.js'),
],
package: 'awcodes/clock-widget'
);
}
}
Step 4: Create the widget
Now we can create our widget. We'll first need to extend Filament's Widget class in our ClockWidget.php file and tell it where to find the view for the widget. Since we are using the PackageServiceProvider to register our views, we can use the :: syntax to tell Filament where to find the view.
use Filament\Widgets\Widget;
class ClockWidget extends Widget
{
protected static string $view = 'clock-widget::widget';
}
Next, we'll need to create the view for our widget. Create a new file at resources/views/widget.blade.php and add the following code. We'll make use of Filament's Blade components to save time on writing the HTML for the widget.
We are using async Alpine to load our Alpine component, so we'll need to add the x-load attribute to the div to tell Alpine to load our component. You can learn more about this in the Core Concepts section of the docs.
<x-filament-widgets::widget>
<x-filament::section>
<x-slot name="heading">
{{ __('clock-widget::clock-widget.title') }}
</x-slot>
<div
x-load
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('clock-widget', 'awcodes/clock-widget') }}"
x-data="clockWidget()"
class="text-center"
>
<p>{{ __('clock-widget::clock-widget.description') }}</p>
<p class="text-xl" x-text="time"></p>
</div>
</x-filament::section>
</x-filament-widgets::widget>
Next, we need to write our Alpine component in src/js/index.js. And build our assets with npm run build.
export default function clockWidget() {
return {
time: new Date().toLocaleTimeString(),
init() {
setInterval(() => {
this.time = new Date().toLocaleTimeString();
}, 1000);
}
}
}
We should also add translations for the text in the widget so users can translate the widget into their language. We'll add the translations to resources/lang/en/widget.php.
return [
'title' => 'Clock Widget',
'description' => 'Your current time is:',
];
Step 5: Update your README
You'll want to update your README.md file to include instructions on how to install your plugin and any other information you want to share with users, Like how to use it in their projects. For example:
// Register the plugin and/or Widget in your Panel provider:
use Awcodes\ClockWidget\ClockWidgetWidget;
public function panel(Panel $panel): Panel
{
return $panel
->widgets([
ClockWidgetWidget::class,
]);
}
And, that's it, our users can now install our plugin and use it in their projects.
=== .ai/01-getting-started rules ===
title: Getting started
import Aside from "@components/Aside.astro"
Introduction
While Filament comes with virtually any tool you'll need to build great apps, sometimes you'll need to add your own functionality either for just your app or as redistributable packages that other developers can include in their own apps. This is why Filament offers a plugin system that allows you to extend its functionality.
Before we dive in, it's important to understand the different contexts in which plugins can be used. There are two main contexts:
- Panel Plugins: These are plugins that are used with Panel Builders. They are typically used only to add functionality when used inside a Panel or as a complete Panel in and of itself. Examples of this are:
- A plugin that adds specific functionality to the dashboard in the form of Widgets.
- A plugin that adds a set of Resources / functionality to an app like a Blog or User Management feature.
- Standalone Plugins: These are plugins that are used in any context outside a Panel Builder. Examples of this are:
- A plugin that adds custom fields to be used with the Form Builders.
- A plugin that adds custom columns or filters to the Table Builders.
Although these are two different mental contexts to keep in mind when building plugins, they can be used together inside the same plugin. They do not have to be mutually exclusive.
Important concepts
Before we dive into the specifics of building plugins, there are a few concepts that are important to understand. You should familiarize yourself with the following before building a plugin:
The Plugin object
Filament introduces the concept of a Plugin object that is used to configure the plugin. This object is a simple PHP class that implements the Filament\Contracts\Plugin interface. This class is used to configure the plugin and is the main entry point for the plugin. It is also used to register Resources and Icons that might be used by your plugin.
While the plugin object is extremely helpful, it is not required to build a plugin. You can still build plugins without using the plugin object as you can see in the building a panel plugin tutorial.
Registering assets
All asset registration, including CSS, JS and Alpine Components, should be done through the plugin's service provider in the packageBooted() method. This allows Filament to register the assets with the Asset Manager and load them when needed.
Creating a plugin
While you can certainly build plugins from scratch, we recommend using the Filament Plugin Skeleton to quickly get started. This skeleton includes all the necessary boilerplate to get you up and running quickly.
Usage
To use the skeleton, simply go to the GitHub repo and click the "Use this template" button. This will create a new repo in your account with the skeleton code. After that, you can clone the repo to your machine. Once you have the code on your machine, navigate to the root of the project and run the following command:
php ./configure.php
This will ask you a series of questions to configure the plugin. Once you've answered all the questions, the script will stub out a new plugin for you, and you can begin to build your amazing new extension for Filament.
Upgrading existing plugins
Since every plugin varies greatly in its scope of use and functionality, there is no one size fits all approaches to upgrading existing plugins. However, one thing to note, that is consistent to all plugins is the deprecation of the PluginServiceProvider.
In your plugin service provider, you will need to change it to extend the PackageServiceProvider instead. You will also need to add a static $name property to the service provider. This property is used to register the plugin with Filament. Here is an example of what your service provider might look like:
class MyPluginServiceProvider extends PackageServiceProvider
{
public static string $name = 'my-plugin';
public function configurePackage(Package $package): void
{
$package->name(static::$name);
}
}
Helpful links
Please read this guide in its entirety before upgrading your plugin. It will help you understand the concepts and how to build your plugin.
=== .ai/02-panel-plugins rules ===
title: Plugin development
Introduction
The basis of Filament plugins are Laravel packages. They are installed into your Filament project via Composer, and follow all the standard techniques, like using service providers to register routes, views, and translations. If you're new to Laravel package development, here are some resources that can help you grasp the core concepts:
- The Package Development section of the Laravel docs serves as a great reference guide.
- Spatie's Package Training course is a good instructional video series to teach you the process step by step.
- Spatie's Package Tools allows you to simplify your service provider classes using a fluent configuration object.
Filament plugins build on top of the concepts of Laravel packages and allow you to ship and consume reusable features for any Filament panel. They can be added to each panel one at a time, and are also configurable differently per-panel.
Configuring the panel with a plugin class
A plugin class is used to allow your package to interact with a panel configuration file. It's a simple PHP class that implements the Plugin interface. 3 methods are required:
- The
getId()method returns the unique identifier of the plugin amongst other plugins. Please ensure that it is specific enough to not clash with other plugins that might be used in the same project. - The
register()method allows you to use any configuration option that is available to the panel. This includes registering resources, custom pages, themes, render hooks and more. - The
boot()method is run only when the panel that the plugin is being registered to is actually in-use. It is executed by a middleware class.
<?php
namespace DanHarrin\FilamentBlog;
use DanHarrin\FilamentBlog\Pages\Settings;
use DanHarrin\FilamentBlog\Resources\CategoryResource;
use DanHarrin\FilamentBlog\Resources\PostResource;
use Filament\Contracts\Plugin;
use Filament\Panel;
class BlogPlugin implements Plugin
{
public function getId(): string
{
return 'blog';
}
public function register(Panel $panel): void
{
$panel
->resources([
PostResource::class,
CategoryResource::class,
])
->pages([
Settings::class,
]);
}
public function boot(Panel $panel): void
{
//
}
}
The users of your plugin can add it to a panel by instantiating the plugin class and passing it to the plugin() method of the configuration:
use DanHarrin\FilamentBlog\BlogPlugin;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->plugin(new BlogPlugin());
}
Fluently instantiating the plugin class
You may want to add a make() method to your plugin class to provide a fluent interface for your users to instantiate it. In addition, by using the container (app()) to instantiate the plugin object, it can be replaced with a different implementation at runtime:
use Filament\Contracts\Plugin;
class BlogPlugin implements Plugin
{
public static function make(): static
{
return app(static::class);
}
// ...
}
Now, your users can use the make() method:
use DanHarrin\FilamentBlog\BlogPlugin;
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->plugin(BlogPlugin::make());
}
Configuring plugins per-panel
You may add other methods to your plugin class, which allow your users to configure it. We suggest that you add both a setter and a getter method for each option you provide. You should use a property to store the preference in the setter and retrieve it again in the getter:
use DanHarrin\FilamentBlog\Resources\AuthorResource;
use Filament\Contracts\Plugin;
use Filament\Panel;
class BlogPlugin implements Plugin
{
protected bool $hasAuthorResource = false;
public function authorResource(bool $condition = true): static
{
// This is the setter method, where the user's preference is
// stored in a property on the plugin object.
$this->hasAuthorResource = $condition;
// The plugin object is returned from the setter method to
// allow fluent chaining of configuration options.
return $this;
}
public function hasAuthorResource(): bool
{
// This is the getter method, where the user's preference
// is retrieved from the plugin property.
return $this->hasAuthorResource;
}
public function register(Panel $panel): void
{
// Since the `register()` method is executed after the user
// configures the plugin, you can access any of their
// preferences inside it.
if ($this->hasAuthorResource()) {
// Here, we only register the author resource on the
// panel if the user has requested it.
$panel->resources([
AuthorResource::class,
]);
}
}
// ...
}
Additionally, you can use the unique ID of the plugin to access any of its configuration options from outside the plugin class. To do this, pass the ID to the filament() method:
filament('blog')->hasAuthorResource()
You may wish to have better type safety and IDE autocompletion when accessing configuration. It's completely up to you how you choose to achieve this, but one idea could be adding a static method to the plugin class to retrieve it:
use Filament\Contracts\Plugin;
class BlogPlugin implements Plugin
{
public static function get(): static
{
return filament(app(static::class)->getId());
}
// ...
}
Now, you can access the plugin configuration using the new static method:
BlogPlugin::get()->hasAuthorResource()
Distributing a panel in a plugin
It's very easy to distribute an entire panel in a Laravel package. This way, a user can simply install your plugin and have an entirely new part of their app pre-built.
When configuring a panel, the configuration class extends the PanelProvider class, and that is a standard Laravel service provider. You can use it as a service provider in your package:
<?php
namespace DanHarrin\FilamentBlog;
use Filament\Panel;
use Filament\PanelProvider;
class BlogPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->id('blog')
->path('blog')
->resources([
// ...
])
->pages([
// ...
])
->widgets([
// ...
])
->middleware([
// ...
])
->authMiddleware([
// ...
]);
}
}
You should then register it as a service provider in the composer.json of your package:
"extra": {
"laravel": {
"providers": [
"DanHarrin\FilamentBlog\BlogPanelProvider"
]
}
}
=== .ai/03-avatar rules ===
title: Avatar Blade component
Introduction
The avatar component is used to render a circular or square image, often used to represent a user or entity as their "profile picture":
<x-filament::avatar
src="https://filamentphp.com/dan.jpg"
alt="Dan Harrin"
/>
Setting the rounding of an avatar
Avatars are fully rounded by default, but you may make them square by setting the circular attribute to false:
<x-filament::avatar
src="https://filamentphp.com/dan.jpg"
alt="Dan Harrin"
:circular="false"
/>
Setting the size of an avatar
By default, the avatar will be "medium" size. You can set the size to either sm, md, or lg using the size attribute:
<x-filament::avatar
src="https://filamentphp.com/dan.jpg"
alt="Dan Harrin"
size="lg"
/>
You can also pass your own custom size classes into the size attribute:
<x-filament::avatar
src="https://filamentphp.com/dan.jpg"
alt="Dan Harrin"
size="w-12 h-12"
/>
=== .ai/02-form rules ===
---
title: Rendering a form in a Blade view
---
import Aside from "@components/Aside.astro"
<Aside variant="warning">
Before proceeding, make sure `filament/forms` is installed in your project. You can check by running:
```bash
composer show filament/forms
```
If it's not installed, consult the [installation guide](../introduction/installation#installing-the-individual-components) and configure the **individual components** according to the instructions.
</Aside>
## Setting up the Livewire component
First, generate a new Livewire component:
```bash
php artisan make:livewire CreatePost
Then, render your Livewire component on the page:
@livewire('create-post')
Alternatively, you can use a full-page Livewire component:
use App\Livewire\CreatePost;
use Illuminate\Support\Facades\Route;
Route::get('posts/create', CreatePost::class);
Adding the form
There are 5 main tasks when adding a form to a Livewire component class. Each one is essential:
- Implement the
HasSchemasinterface and use theInteractsWithSchemastrait. - Define a public Livewire property to store your form's data. In our example, we'll call this
$data, but you can call it whatever you want. - Add a
form()method, which is where you configure the form. Add the form's schema, and tell Filament to store the form data in the$dataproperty (usingstatePath('data')). - Initialize the form with
$this->form->fill()inmount(). This is imperative for every form that you build, even if it doesn't have any initial data. - Define a method to handle the form submission. In our example, we'll call this
create(), but you can call it whatever you want. Inside that method, you can validate and get the form's data using$this->form->getState(). It's important that you use this method instead of accessing the$this->dataproperty directly, because the form's data needs to be validated and transformed into a useful format before being returned.
<?php
namespace App\Livewire;
use Filament\Forms\Components\MarkdownEditor;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Concerns\InteractsWithSchemas;
use Filament\Schemas\Contracts\HasSchemas;
use Illuminate\Contracts\View\View;
use Filament\Schemas\Schema;
use Livewire\Component;
class CreatePost extends Component implements HasSchemas
{
use InteractsWithSchemas;
public ?array $data = [];
public function mount(): void
{
$this->form->fill();
}
public function form(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('title')
->required(),
MarkdownEditor::make('content'),
// ...
])
->statePath('data');
}
public function create(): void
{
dd($this->form->getState());
}
public function render(): View
{
return view('livewire.create-post');
}
}
Finally, in your Livewire component's view, render the form:
<div>
<form wire:submit="create">
{{ $this->form }}
<button type="submit">
Submit
</button>
</form>
<x-filament-actions::modals />
</div>
Visit your Livewire component in the browser, and you should see the form components from components():
Submit the form with data, and you'll see the form's data dumped to the screen. You can save the data to a model instead of dumping it:
use App\Models\Post;
public function create(): void
{
Post::create($this->form->getState());
}
- `filament/actions`
- `filament/schemas`
- `filament/support`
These packages allow you to use their components within Livewire components.
For example, if your form uses [Actions](../actions), remember to implement the `HasActions` interface and use the `InteractsWithActions` trait on your Livewire component class.
If you are using any other [Filament components](overview#package-components) in your form, make sure to install and integrate the corresponding package as well.
Initializing the form with data
To fill the form with data, just pass that data to the $this->form->fill() method. For example, if you're editing an existing post, you might do something like this:
use App\Models\Post;
public function mount(Post $post): void
{
$this->form->fill($post->attributesToArray());
}
It's important that you use the $this->form->fill() method instead of assigning the data directly to the $this->data property. This is because the post's data needs to be internally transformed into a useful format before being stored.
Setting a form model
Giving the $form access to a model is useful for a few reasons:
- It allows fields within that form to load information from that model. For example, select fields can load their options from the database automatically.
- The form can load and save the model's relationship data automatically. For example, you have an Edit Post form, with a Repeater which manages comments associated with that post. Filament will automatically load the comments for that post when you call
$this->form->fill([...]), and save them back to the relationship when you call$this->form->getState(). - Validation rules like
exists()andunique()can automatically retrieve the database table name from the model.
It is advised to always pass the model to the form when there is one. As explained, it unlocks many new powers of Filament's form system.
To pass the model to the form, use the $form->model() method:
use Filament\Schemas\Schema;
public Post $post;
public function form(Schema $schema): Schema
{
return $schema
->components([
// ...
])
->statePath('data')
->model($this->post);
}
Passing the form model after the form has been submitted
In some cases, the form's model is not available until the form has been submitted. For example, in a Create Post form, the post does not exist until the form has been submitted. Therefore, you can't pass it in to $form->model(). However, you can pass a model class instead:
use App\Models\Post;
use Filament\Schemas\Schema;
public function form(Schema $schema): Schema
{
return $schema
->components([
// ...
])
->statePath('data')
->model(Post::class);
}
On its own, this isn't as powerful as passing a model instance. For example, relationships won't be saved to the post after it is created. To do that, you'll need to pass the post to the form after it has been created, and call saveRelationships() to save the relationships to it:
use App\Models\Post;
public function create(): void
{
$post = Post::create($this->form->getState());
// Save the relationships from the form to the post after it is created.
$this->form->model($post)->saveRelationships();
}
Saving form data to individual properties
In all of our previous examples, we've been saving the form's data to the public $data property on the Livewire component. However, you can save the data to individual properties instead. For example, if you have a form with a title field, you can save the form's data to the $title property instead. To do this, don't pass a statePath() to the form at all. Ensure that all of your fields have their own public properties on the class.
use Filament\Forms\Components\MarkdownEditor;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
public ?string $title = null;
public ?string $content = null;
public function form(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('title')
->required(),
MarkdownEditor::make('content'),
// ...
]);
}
Using multiple forms
Many forms can be defined using the InteractsWithSchemas trait. Each of the forms should use a method with the same name:
use Filament\Forms\Components\MarkdownEditor;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
public ?array $postData = [];
public ?array $commentData = [];
public function editPostForm(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('title')
->required(),
MarkdownEditor::make('content'),
// ...
])
->statePath('postData')
->model($this->post);
}
public function createCommentForm(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('name')
->required(),
TextInput::make('email')
->email()
->required(),
MarkdownEditor::make('content')
->required(),
// ...
])
->statePath('commentData')
->model(Comment::class);
}
Now, each form is addressable by its name instead of form. For example, to fill the post form, you can use $this->editPostForm->fill([...]), or to get the data from the comment form you can use $this->createCommentForm->getState().
You'll notice that each form has its own unique statePath(). Each form will write its state to a different array on your Livewire component, so it's important to define the public properties, $postData and $commentData in this example.
Resetting a form's data
You can reset a form back to its default data at any time by calling $this->form->fill(). For example, you may wish to clear the contents of a form every time it's submitted:
use App\Models\Comment;
public function createComment(): void
{
Comment::create($this->form->getState());
// Reinitialize the form to clear its data.
$this->form->fill();
}
Generating form Livewire components with the CLI
It's advised that you learn how to set up a Livewire component with forms manually, but once you are confident, you can use the CLI to generate a form for you.
php artisan make:filament-livewire-form RegistrationForm
This will generate a new app/Livewire/RegistrationForm.php component, which you can customize.
Generating a form for an Eloquent model
Filament is also able to generate forms for a specific Eloquent model. These are more powerful, as they will automatically save the data in the form for you, and ensure the form fields are properly configured to access that model.
When generating a form with the make:livewire-form command, it will ask for the name of the model:
php artisan make:filament-livewire-form Products/CreateProduct
Generating an edit form for an Eloquent record
By default, passing a model to the make:livewire-form command will result in a form that creates a new record in your database. If you pass the --edit flag to the command, it will generate an edit form for a specific record. This will automatically fill the form with the data from the record, and save the data back to the model when the form is submitted.
php artisan make:filament-livewire-form Products/EditProduct --edit
Automatically generating form schemas
Filament is also able to guess which form fields you want in the schema, based on the model's database columns. You can use the --generate flag when generating your form:
php artisan make:filament-livewire-form Products/CreateProduct --generate
=== .ai/03-input rules ===
title: Input Blade component
Introduction
The input component is a wrapper around the native <input> element. It provides a simple interface for entering a single line of text.
<x-filament::input.wrapper>
<x-filament::input
type="text"
wire:model="name"
/>
</x-filament::input.wrapper>
To use the input component, you must wrap it in an "input wrapper" component, which provides a border and other elements such as a prefix or suffix. You can learn more about customizing the input wrapper component here.
=== .ai/03-dropdown rules ===
title: Dropdown Blade component
Introduction
The dropdown component allows you to render a dropdown menu with a button that triggers it:
<x-filament::dropdown>
<x-slot name="trigger">
<x-filament::button>
More actions
</x-filament::button>
</x-slot>
<x-filament::dropdown.list>
<x-filament::dropdown.list.item wire:click="openViewModal">
View
</x-filament::dropdown.list.item>
<x-filament::dropdown.list.item wire:click="openEditModal">
Edit
</x-filament::dropdown.list.item>
<x-filament::dropdown.list.item wire:click="openDeleteModal">
Delete
</x-filament::dropdown.list.item>
</x-filament::dropdown.list>
</x-filament::dropdown>
Using a dropdown item as an anchor link
By default, a dropdown item's underlying HTML tag is <button>. You can change it to be an <a> tag by using the tag attribute:
<x-filament::dropdown.list.item
href="https://filamentphp.com"
tag="a"
>
Filament
</x-filament::dropdown.list.item>
Changing the color of a dropdown item
By default, the color of a dropdown item is "gray". You can change it to be danger, info, primary, success or warning by using the color attribute:
<x-filament::dropdown.list.item color="danger">
Edit
</x-filament::dropdown.list.item>
<x-filament::dropdown.list.item color="info">
Edit
</x-filament::dropdown.list.item>
<x-filament::dropdown.list.item color="primary">
Edit
</x-filament::dropdown.list.item>
<x-filament::dropdown.list.item color="success">
Edit
</x-filament::dropdown.list.item>
<x-filament::dropdown.list.item color="warning">
Edit
</x-filament::dropdown.list.item>
Adding an icon to a dropdown item
You can add an icon to a dropdown item by using the icon attribute:
<x-filament::dropdown.list.item icon="heroicon-m-pencil">
Edit
</x-filament::dropdown.list.item>
Changing the icon color of a dropdown item
By default, the icon color uses the same color as the item itself. You can override it to be danger, info, primary, success or warning by using the icon-color attribute:
<x-filament::dropdown.list.item icon="heroicon-m-pencil" icon-color="danger">
Edit
</x-filament::dropdown.list.item>
<x-filament::dropdown.list.item icon="heroicon-m-pencil" icon-color="info">
Edit
</x-filament::dropdown.list.item>
<x-filament::dropdown.list.item icon="heroicon-m-pencil" icon-color="primary">
Edit
</x-filament::dropdown.list.item>
<x-filament::dropdown.list.item icon="heroicon-m-pencil" icon-color="success">
Edit
</x-filament::dropdown.list.item>
<x-filament::dropdown.list.item icon="heroicon-m-pencil" icon-color="warning">
Edit
</x-filament::dropdown.list.item>
Adding an image to a dropdown item
You can add a circular image to a dropdown item by using the image attribute:
<x-filament::dropdown.list.item image="https://filamentphp.com/dan.jpg">
Dan Harrin
</x-filament::dropdown.list.item>
Adding a badge to a dropdown item
You can render a badge on top of a dropdown item by using the badge slot:
<x-filament::dropdown.list.item>
Mark notifications as read
<x-slot name="badge">
3
</x-slot>
</x-filament::dropdown.list.item>
You can change the color of the badge using the badge-color attribute:
<x-filament::dropdown.list.item badge-color="danger">
Mark notifications as read
<x-slot name="badge">
3
</x-slot>
</x-filament::dropdown.list.item>
Setting the placement of a dropdown
The dropdown may be positioned relative to the trigger button by using the placement attribute:
<x-filament::dropdown placement="top-start">
{{-- Dropdown items --}}
</x-filament::dropdown>
Setting the width of a dropdown
The dropdown may be set to a width by using the width attribute. Options correspond to Tailwind's max-width scale. The options are xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl and 7xl:
<x-filament::dropdown width="xs">
{{-- Dropdown items --}}
</x-filament::dropdown>
Controlling the maximum height of a dropdown
The dropdown content can have a maximum height using the max-height attribute, so that it scrolls. You can pass a CSS length:
<x-filament::dropdown max-height="400px">
{{-- Dropdown items --}}
</x-filament::dropdown>
=== .ai/02-widget rules ===
title: Rendering a widget in a Blade view
import Aside from "@components/Aside.astro"
```bash
composer show filament/widgets
```
If it's not installed, consult the [installation guide](../introduction/installation#installing-the-individual-components) and configure the **individual components** according to the instructions.
Creating a widget
Use the make:filament-widget command to generate a new widget. For details on customization and usage, see the widgets section.
Adding the widget
Since widgets are Livewire components, you can easily render a widget in any Blade view using the @livewire directive:
<div>
@livewire(\App\Livewire\Dashboard\PostsChart::class)
</div>
=== .ai/03-pagination rules ===
title: Pagination Blade component
Introduction
The pagination component can be used in a Livewire Blade view only. It can render a list of paginated links:
use App\Models\User;
use Illuminate\Contracts\View\View;
use Livewire\Component;
class ListUsers extends Component
{
// ...
public function render(): View
{
return view('livewire.list-users', [
'users' => User::query()->paginate(10),
]);
}
}
<x-filament::pagination :paginator="$users" />
Alternatively, you can use simple pagination or cursor pagination, which will just render a "previous" and "next" button:
use App\Models\User;
User::query()->simplePaginate(10)
User::query()->cursorPaginate(10)
Allowing the user to customize the number of items per page
You can allow the user to customize the number of items per page by passing an array of options to the page-options attribute. You must also define a Livewire property where the user's selection will be stored:
use App\Models\User;
use Illuminate\Contracts\View\View;
use Livewire\Component;
class ListUsers extends Component
{
public int | string $perPage = 10;
// ...
public function render(): View
{
return view('livewire.list-users', [
'users' => User::query()->paginate($this->perPage),
]);
}
}
<x-filament::pagination
:paginator="$users"
:page-options="[5, 10, 20, 50, 100, 'all']"
current-page-option-property="perPage"
/>
Displaying links to the first and the last page
Extreme links are the first and last page links. You can add them by passing the extreme-links attribute to the component:
<x-filament::pagination
:paginator="$users"
extreme-links
/>
=== .ai/03-fieldset rules ===
title: Fieldset Blade component
Introduction
You can use a fieldset to group multiple form fields together, optionally with a label:
<x-filament::fieldset>
<x-slot name="label">
Address
</x-slot>
{{-- Form fields --}}
</x-filament::fieldset>
=== .ai/03-section rules ===
title: Section Blade component
Introduction
A section can be used to group content together, with an optional heading:
<x-filament::section>
<x-slot name="heading">
User details
</x-slot>
{{-- Content --}}
</x-filament::section>
Adding a description to the section
You can add a description below the heading to the section by using the description slot:
<x-filament::section>
<x-slot name="heading">
User details
</x-slot>
<x-slot name="description">
This is all the information we hold about the user.
</x-slot>
{{-- Content --}}
</x-filament::section>
Adding an icon to the section header
You can add an icon to a section by using the icon attribute:
<x-filament::section icon="heroicon-o-user">
<x-slot name="heading">
User details
</x-slot>
{{-- Content --}}
</x-filament::section>
Changing the color of the section icon
By default, the color of the section icon is "gray". You can change it to be danger, info, primary, success or warning by using the icon-color attribute:
<x-filament::section
icon="heroicon-o-user"
icon-color="info"
>
<x-slot name="heading">
User details
</x-slot>
{{-- Content --}}
</x-filament::section>
Changing the size of the section icon
By default, the size of the section icon is "large". You can change it to be "small" or "medium" by using the icon-size attribute:
<x-filament::section
icon="heroicon-m-user"
icon-size="sm"
>
<x-slot name="heading">
User details
</x-slot>
{{-- Content --}}
</x-filament::section>
<x-filament::section
icon="heroicon-m-user"
icon-size="md"
>
<x-slot name="heading">
User details
</x-slot>
{{-- Content --}}
</x-filament::section>
Adding content to the end of the header
You may render additional content at the end of the header, next to the heading and description, using the afterHeader slot:
<x-filament::section>
<x-slot name="heading">
User details
</x-slot>
<x-slot name="afterHeader">
{{-- Input to select the user's ID --}}
</x-slot>
{{-- Content --}}
</x-filament::section>
Making a section collapsible
You can make the content of a section collapsible by using the collapsible attribute:
<x-filament::section collapsible>
<x-slot name="heading">
User details
</x-slot>
{{-- Content --}}
</x-filament::section>
Making a section collapsed by default
You can make a section collapsed by default by using the collapsed attribute:
<x-filament::section
collapsible
collapsed
>
<x-slot name="heading">
User details
</x-slot>
{{-- Content --}}
</x-filament::section>
Persisting collapsed sections
You can persist whether a section is collapsed in local storage using the persist-collapsed attribute, so it will remain collapsed when the user refreshes the page. You will also need a unique id attribute to identify the section from others, so that each section can persist its own collapse state:
<x-filament::section
collapsible
collapsed
persist-collapsed
id="user-details"
>
<x-slot name="heading">
User details
</x-slot>
{{-- Content --}}
</x-filament::section>
Adding the section header aside the content instead of above it
You can change the position of the section header to be aside the content instead of above it by using the aside attribute:
<x-filament::section aside>
<x-slot name="heading">
User details
</x-slot>
{{-- Content --}}
</x-filament::section>
Positioning the content before the header
You can change the position of the content to be before the header instead of after it by using the content-before attribute:
<x-filament::section
aside
content-before
>
<x-slot name="heading">
User details
</x-slot>
{{-- Content --}}
</x-filament::section>
=== .ai/03-breadcrumbs rules ===
title: Breadcrumbs Blade component
Introduction
The breadcrumbs component is used to render a simple, linear navigation that informs the user of their current location within the application:
<x-filament::breadcrumbs :breadcrumbs="[
'/' => 'Home',
'/dashboard' => 'Dashboard',
'/dashboard/users' => 'Users',
'/dashboard/users/create' => 'Create User',
]" />
The keys of the array are URLs that the user is able to click on to navigate, and the values are the text that will be displayed for each link.
=== .ai/03-button rules ===
title: Button Blade component
Introduction
The button component is used to render a clickable button that can perform an action:
<x-filament::button wire:click="openNewUserModal">
New user
</x-filament::button>
Using a button as an anchor link
By default, a button's underlying HTML tag is <button>. You can change it to be an <a> tag by using the tag attribute:
<x-filament::button
href="https://filamentphp.com"
tag="a"
>
Filament
</x-filament::button>
Setting the size of a button
By default, the size of a button is "medium". You can make it "extra small", "small", "large" or "extra large" by using the size attribute:
<x-filament::button size="xs">
New user
</x-filament::button>
<x-filament::button size="sm">
New user
</x-filament::button>
<x-filament::button size="lg">
New user
</x-filament::button>
<x-filament::button size="xl">
New user
</x-filament::button>
Changing the color of a button
By default, the color of a button is "primary". You can change it to be danger, gray, info, success or warning by using the color attribute:
<x-filament::button color="danger">
New user
</x-filament::button>
<x-filament::button color="gray">
New user
</x-filament::button>
<x-filament::button color="info">
New user
</x-filament::button>
<x-filament::button color="success">
New user
</x-filament::button>
<x-filament::button color="warning">
New user
</x-filament::button>
Adding an icon to a button
You can add an icon to a button by using the icon attribute:
<x-filament::button icon="heroicon-m-sparkles">
New user
</x-filament::button>
You can also change the icon's position to be after the text instead of before it, using the icon-position attribute:
<x-filament::button
icon="heroicon-m-sparkles"
icon-position="after"
>
New user
</x-filament::button>
Making a button outlined
You can make a button use an "outlined" design using the outlined attribute:
<x-filament::button outlined>
New user
</x-filament::button>
Adding a tooltip to a button
You can add a tooltip to a button by using the tooltip attribute:
<x-filament::button tooltip="Register a user">
New user
</x-filament::button>
Adding a badge to a button
You can render a badge on top of a button by using the badge slot:
<x-filament::button>
Mark notifications as read
<x-slot name="badge">
3
</x-slot>
</x-filament::button>
You can change the color of the badge using the badge-color attribute:
<x-filament::button badge-color="danger">
Mark notifications as read
<x-slot name="badge">
3
</x-slot>
</x-filament::button>
=== .ai/03-select rules ===
title: Select Blade component
Introduction
The select component is a wrapper around the native <select> element. It provides a simple interface for selecting a single value from a list of options:
<x-filament::input.wrapper>
<x-filament::input.select wire:model="status">
<option value="draft">Draft</option>
<option value="reviewing">Reviewing</option>
<option value="published">Published</option>
</x-filament::input.select>
</x-filament::input.wrapper>
To use the select component, you must wrap it in an "input wrapper" component, which provides a border and other elements such as a prefix or suffix. You can learn more about customizing the input wrapper component here.
=== .ai/03-badge rules ===
title: Badge Blade component
Introduction
The badge component is used to render a small box with some text inside:
<x-filament::badge>
New
</x-filament::badge>
Setting the size of a badge
By default, the size of a badge is "medium". You can make it "extra small" or "small" by using the size attribute:
<x-filament::badge size="xs">
New
</x-filament::badge>
<x-filament::badge size="sm">
New
</x-filament::badge>
Changing the color of the badge
By default, the color of a badge is "primary". You can change it to be danger, gray, info, success or warning by using the color attribute:
<x-filament::badge color="danger">
New
</x-filament::badge>
<x-filament::badge color="gray">
New
</x-filament::badge>
<x-filament::badge color="info">
New
</x-filament::badge>
<x-filament::badge color="success">
New
</x-filament::badge>
<x-filament::badge color="warning">
New
</x-filament::badge>
Adding an icon to a badge
You can add an [icon](.
Truncated - read the full file at https://github.com/blhk0532/nds-livewire/blob/c7884767516b2d5f12bb96add20e833955275d51/.cursor/rules/laravel-boost.mdc.