Courses

Filament 3 From Scratch: Practical Course

Column Formatting: Badges, URLs, Labels, Alignment, Dates

Summary of this lesson:
- Adding badges/tags to columns
- Creating clickable URL columns
- Customizing column labels
- Formatting date fields

Filament Table Builder has dozens of ways how to customize column values. In this lesson, I will touch on the most commonly used ones.


Show as Tags/Badges

Filament has a general concept of a "Badge", I personally call it a "Tag", which is just a visual representation of a text/number surrounded by a border.

Let's take a look at it by just adding ->badge() to two columns in our Products table: status and tags.

return $table
->columns([
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('price'),
Tables\Columns\TextColumn::make('status')
->badge(),
Tables\Columns\TextColumn::make('category.name'),
Tables\Columns\TextColumn::make('tags.name')
->badge(),
])

Here's the visual result:

As you can see, Filament automatically wraps the text as a "badge". Not only does it work for a simple TextColumn, but also, in the case of the belongsToMany relationship, it shows multiple badges instead of a comma-separated text.

You can also specify different colors for different badge values.

Tables\Columns\TextColumn::make('status')
->badge()
->color(fn (string $state): string => match ($state) {
'in stock' => 'primary',
'sold out' => 'danger',
'coming soon' => 'info',
}),

Here's how it looks now:

You can choose the colors from these options:

  • danger
  • gray
  • info
  • primary
  • success
  • warning

Clickable URL Column

You would often want some text to act as a link to another section or page. For example, the Product "Category" column could link to the Edit Category page or to a page of Products filtered by that Category.

For that, you can add the ->url() method to your column.

Tables\Columns\TextColumn::make('category.name')
->url(fn (Product $product): string => CategoryResource::getUrl('edit', ['record' => $product->category_id])),

Notice: to be honest, I prefer the short PHP functions syntax only if the line of code is actually short. In this case, for readability, I would choose this:

Tables\Columns\TextColumn::make('category.name')
->url(function (Product $product): string {
return CategoryResource::getUrl('edit', [
'record' => $product->category_id
]);
}),

And then, clicking on the category name would lead to the page something like /admin/categories/1/edit or similar. In our project, we don't have the page for Category Edit, as we generated that resource as --simple, so this was just an illustrative example.

As you can see, you can get the URL to any of the Filament Resources pages with the method getUrl():

  • CategoryResource::getUrl();: returns /admin/categories
  • CategoryResource::getUrl('create');: returns /admin/categories/create
  • CategoryResource::getUrl('edit', ['record' => 1]);: returns /admin/categories/1/edit

Different Label on Top

By default, Filament is trying to "guess" the column name in the header of the table from the field name, just putting the Title Case:

  • TextColumn::make('name') -> "Name"
  • TextColumn::make('category.name') -> "Category"
  • TextColumn::make('tags.name') -> "Tags"

But you can easily override it just by adding ->label() to the chained methods.

Tables\Columns\TextColumn::make('category.name')
->label('Category name'),

Here's the visual result:


Column Alignment

Also, you can specify a different alignment than the default "left". Again, it's as easy as adding a method to the chain:

Tables\Columns\TextColumn::make('price')
->sortable()
->money('usd')
->getStateUsing(function (Product $record): float {
return $record->price / 100;
})
->alignEnd(),

Shorter methods for each possible alignment:

  • alignLeft()
  • alignCenter()
  • alignRight()
  • alignJustify()
  • alignStart()
  • alignEnd()

Or, you can use a longer syntax with one standard method and specific Enum constants for the alignments:

use Filament\Support\Enums\Alignment;
 
// ...
Tables\Columns\TextColumn::make('price')
->alignment(Alignment::End)

Date Fields Formatting

If you want to show a timestamp field like created_at, you may choose which part to show: full date and time, only date, or "x days ago".

Just pick one of the methods to attach to the chain with the optional format parameter:

Tables\Columns\TextColumn::make('created_at')
->dateTime(),

Tables\Columns\TextColumn::make('created_at')
->dateTime('m/d/Y H:i'),

Tables\Columns\TextColumn::make('created_at')
->date(),

// This will run "diffForHumans" under the hood
Tables\Columns\TextColumn::make('created_at')
->since(),


These are just a few typical customizations of the table columns. For more examples and methods, please refer to the official documentation.

Previous: Table Filters for Select and Dates
avatar

Hello Povilas, first I would like to thank you for this amazing content. I've followed part of this course, and I've a little problem and you doesn't talk about that and probably you never tested it. I've a file upload at form like this: Forms\Components\FileUpload::make('avatarurl') ->image() ->disk('private') ->visibility('private') ->directory('profile-photos') ->maxSize(1024) ->imageResizeMode('cover') ->getUploadedFileNameForStorageUsing( fn(TemporaryUploadedFile $file): string => (string)str($file->getClientOriginalName()) ->prepend('profile-'), ),

And at table I've this: Tables\Columns\ImageColumn::make('avatarurl')->circular() ->disk('private') ->visibility('private'),

and the final result is I can't show the image on the table. If I've the visibility 'public' everything is ok. Can you help me? Thank You!

avatar

Hi Pedro. Well, to me it's pretty obvious, if you want the file to be PRIVATE, it means you're HIDING it from everyone, including the Filament table. What are you actually trying to achieve here? Why private?

avatar

For now i was testing with images, but in the future it can be private documents for example. And filament documentation talks about this "Filament can generate temporary URLs to render private images, you may set the visibility() to private" (https://filamentphp.com/docs/3.x/tables/columns/image#managing-the-image-disk).

Another example is, is case of images they are inside a panel, if I've the image url and I'am not logged in, it doesn't make sense to be able to access the image. What is your opinion?

avatar

Well it's still a weird scenario to me, personally. I understand that Filament may allow it, but as you can see it doesn't work properly, for some reason, I haven't debugged it, personally.

I probably need to schedule some time on a proper tutorial about private/public file uploads in Filament, it's a deep topic with many use cases. Adding to the (already huge) to-do list.

avatar

I was going to search at github filament pull request, some people have this problems with S3.

avatar

When I add the URL for the category, and I click the link, show error: Missing required parameter for [Route: filament.admin.resources.categories.edit] [URI: admin/categories/{record}/edit] [Missing parameter: record].

avatar

@Zhtekdev I initially got it too, but you can see a note saying: "In our project, we don't have the page for Category Edit, as we generated that resource as --simple, so this was just an illustrative example." That's why you get the error.

avatar
You can use Markdown
avatar

The URL examples you show all reference other Filament desintations. Is it possible to fall back to Laravel route() functions in the url() method? Would it need a callback to reference the current model?

avatar
You can use Markdown
avatar

Tables\Columns\TextColumn::make('category.name') ->url(fn (Product $product): string => CategoryResource::getUrl('create', ['record' => $product->category_id])),

	After doing this and the create page opens, how do i access the variable category_ID in the form?
avatar

I'm not sure about the setup nor the URL that opens, but if it's a url with query parameter in it - you can simply do request()->query('record') and that should do the trick

avatar

Thanks, worked perfectly.

avatar
You can use Markdown
avatar

If category is simple resource, this not works! because does have edit route!

👍 1
avatar
You can use Markdown
avatar

the route to category edit won't work because we are using modal for editing the categories.

avatar
You can use Markdown
avatar
You can use Markdown