<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: BuildWithTall</title>
    <description>The latest articles on Forem by BuildWithTall (@tallcms).</description>
    <link>https://forem.com/tallcms</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3861098%2F1ace6125-3967-465d-bf2c-2d548ea30d28.jpeg</url>
      <title>Forem: BuildWithTall</title>
      <link>https://forem.com/tallcms</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tallcms"/>
    <language>en</language>
    <item>
      <title>How to Extend Filament v5's Register Page</title>
      <dc:creator>BuildWithTall</dc:creator>
      <pubDate>Fri, 01 May 2026 13:41:43 +0000</pubDate>
      <link>https://forem.com/tallcms/how-to-extend-filament-v5s-register-page-4g5h</link>
      <guid>https://forem.com/tallcms/how-to-extend-filament-v5s-register-page-4g5h</guid>
      <description>&lt;p&gt;Filament v5 ships a native register page. Some plugins ignore it and roll their own controller. Here's how we added captcha, honeypot, role assignment, and event bridging — entirely through Filament's documented hooks, without touching &lt;code&gt;register()&lt;/code&gt; itself.&lt;/p&gt;

&lt;p&gt;Filament is one of the best backend frameworks for Laravel. It builds admin panels fast, looks great out of the box, and is deeply extensible. But most non-trivial projects don't stop at the backend. If you're building a SaaS, a web app with a public-facing portion, or anything with real users signing up, you eventually need authentication that lives outside the admin shell.&lt;/p&gt;

&lt;p&gt;A common answer is to reach for one of Laravel's official starter kits. The modern lineup — React, Vue, and Livewire flavours, all with auth scaffolding baked in — is the path most teams take today, and earlier kits like Breeze and Jetstream from the Laravel 10.x era are still maintained and widely deployed. They're battle-tested, beautifully documented, and a great fit for many projects: they handle public-facing auth completely while letting Filament focus on the admin side. For plenty of teams that separation is exactly what you want, and these kits absolutely deserve their place in the Laravel ecosystem.&lt;/p&gt;

&lt;p&gt;That said, if you'd rather harden your authentication and roll your own using Filament, you don't need to reach outside the framework — Filament v5 already has everything you need to extend. It ships a complete auth flow — \Filament\Auth\Pages\Login, \Filament\Auth\Pages\Register, email verification, password reset, and multi-factor authentication — and these aren't just admin-panel utilities. They're real, themable, extensible pages you can put in front of your users.&lt;/p&gt;

&lt;p&gt;The catch: the register page in particular needs work before it's production-ready. Out of the box there's no captcha, no honeypot, no role assignment, no Laravel-event bridge for listeners that other parts of your app already depend on. Most plugins solve this by reaching for a custom Laravel controller, a Blade form, and manual validation — abandoning Filament's auth flow entirely.&lt;/p&gt;

&lt;p&gt;We took the other approach while building tallcms/filament-registration(Link). Every production feature — captcha, honeypot, rate limiting, default role assignment, post-registration redirect — landed inside Filament's documented extension points. We never overrode register(). These patterns apply to any Filament v5 project that needs a public register page without leaving the framework.&lt;/p&gt;

&lt;p&gt;The Core Idea: Two Hooks, Not One Override&lt;br&gt;
Filament's Register page exposes two extension points :&lt;/p&gt;

&lt;p&gt;mutateFormDataBeforeRegister(array $data): array — runs after validation but before User::create(). The place for anything that should gate registration.&lt;/p&gt;

&lt;p&gt;handleRegistration(array $data): Model — wraps user creation. The place for anything that should run after the user exists.&lt;/p&gt;

&lt;p&gt;The temptation, especially if you're porting from a legacy controller, is to override register() and rebuild everything inside one method. Don't. Filament's register() already orchestrates throttling, validation, email verification, response dispatch, and event firing. You won't get all those right by rewriting it. You don't need to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Register&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;\Filament\Auth\Pages\Register&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Schema&lt;/span&gt; &lt;span class="nv"&gt;$schema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Schema&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$schema&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;components&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getNameFormComponent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getEmailFormComponent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPasswordFormComponent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPasswordConfirmationFormComponent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;HoneypotField&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;CaptchaField&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;mutateFormDataBeforeRegister&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;checkHoneypot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;throttleCaptcha&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;verifyCaptcha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;unset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;honeypotField&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tokenField&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handleRegistration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;handleRegistration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;maybeMarkEmailVerified&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;maybeAssignDefaultRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Auth\Events\Registered&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's the entire shape. Everything below is what goes inside those methods.&lt;/p&gt;

&lt;p&gt;Layer 1: Honeypot via Validation, Not Stealth&lt;br&gt;
The legacy approach to honeypots is to silently render a fake success page when the field is filled, hoping the bot logs a false positive. It works, until it doesn't — modern bots roll their own success-page detectors.&lt;/p&gt;

&lt;p&gt;A cleaner approach inside Filament: throw a ValidationException. Filament catches Laravel's validation exceptions automatically and surfaces the message on the form. Bots that don't fill the honeypot get through validation; bots that do see a generic "Bot check failed" message attached to the honeypot field.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;honeypotField&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;ValidationException&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;withMessages&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;honeypotField&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bot check failed. Please try again.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Layer 2: Rate-Limit Before You Verify&lt;br&gt;
Layer 3: Pluggable Captcha via a Contract&lt;br&gt;
Layer 4: Two-Layer Config (Env + Admin UI)&lt;br&gt;
Layer 5: Container-Bound Redirects&lt;/p&gt;

&lt;p&gt;We have shared this in details on this blog&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://tallcms.com/blog/going-full-native-how-to-extend-filament-v5s-register-page" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpropertypages-storage.s3.ap-southeast-1.amazonaws.com%2Fcms%2Fposts%2Ffeatured-images%2F01KQBDPJ3EB2T1RKQWXW67B0TQ.svg" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://tallcms.com/blog/going-full-native-how-to-extend-filament-v5s-register-page" rel="noopener noreferrer" class="c-link"&gt;
            Going Full Native: How to Extend Filament v5's Register Page
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Filament v5 ships a native register page. Some plugins ignore it and roll their own controller. Here's how we added captcha, honeypot, role assignment, and event bridging — entirely through Filament's documented hooks, without touching `register()` itself.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
          tallcms.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>filament</category>
      <category>laravel</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Turn Filament v5's Rich Editor Into a Full Block Editor</title>
      <dc:creator>BuildWithTall</dc:creator>
      <pubDate>Wed, 29 Apr 2026 03:43:26 +0000</pubDate>
      <link>https://forem.com/tallcms/how-to-turn-filament-v5s-rich-editor-into-a-full-block-editor-3g5g</link>
      <guid>https://forem.com/tallcms/how-to-turn-filament-v5s-rich-editor-into-a-full-block-editor-3g5g</guid>
      <description>&lt;p&gt;Filament v5's RichEditor is powerful out of the box. It is built on Tiptap, which means it's natively extensible, and it now supports *custom blocks out of the box . Here's how to extend it into a categorized, searchable block editor — without forking a single line of Filament core.&lt;/p&gt;

&lt;p&gt;With the release of Filament v5, one of the biggest improvements is the switch to a Tiptap-powered Rich Editor which means it's natively extensible, and crucially it now supports custom blocks out of the box via the RichContentCustomBlock base class.&lt;/p&gt;

&lt;p&gt;This is a huge deal. You can define structured, configurable content blocks that live inside the rich text flow — not bolted on as a separate Builder field, but inline with your prose. Each block gets a unique ID, a modal form schema, and preview/render methods. For most applications, this is more than enough.&lt;/p&gt;

&lt;p&gt;But once you're past a handful of blocks — say you've got 15+ block types across categories like Content, Media, Social Proof, and Forms — the default flat list becomes unworkable. There's no search, no categorization, no visual cues to tell a Hero apart from a FAQ. Your editors start scrolling through a wall of block names.&lt;/p&gt;

&lt;p&gt;While building a Laravel CMS, we hit exactly this wall. Rather than build a page builder from scratch, we extended Filament's block system layer by layer. These patterns are applicable to any Filament project with non-trivial block usage — CMS or not.&lt;/p&gt;

&lt;p&gt;Layer 1: Extending the Editor Component&lt;/p&gt;

&lt;p&gt;The first step is surprisingly clean. Create a class that extends RichEditor and swap in your own Blade view:&lt;/p&gt;

&lt;p&gt;`&amp;lt;?&lt;br&gt;
class EnhancedRichEditor extends RichEditor&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
    protected function setUp(): void&lt;br&gt;
    {&lt;br&gt;
        parent::setUp();&lt;br&gt;
        if (static::isFilamentCompatible()) {&lt;br&gt;
            $this-&amp;gt;view = 'filament.forms.components.enhanced-rich-editor';&lt;br&gt;
        }&lt;br&gt;
    }&lt;br&gt;
}`&lt;br&gt;
No monkey-patching, no service container tricks. Filament lets you override the view on any form component, so you get full control over the block panel UI while the editor internals stay untouched. The version check provides a graceful fallback to the standard panel on unsupported versions.&lt;/p&gt;

&lt;p&gt;Layer 2: Auto-Discovery With Override Precedence&lt;/p&gt;

&lt;p&gt;If your project is a package or supports plugins, you'll want blocks from multiple sources. A discovery service with reflection-based auto-discovery solves this — scan directories for classes extending RichContentCustomBlock, then deduplicate by block ID so that later sources override earlier ones:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Package blocks&lt;/strong&gt; (base defaults)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;App blocks&lt;/strong&gt; (developer overrides)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugin blocks&lt;/strong&gt; (override all)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A developer drops a CallToActionBlock in their app/Filament/Blocks/ directory and it automatically replaces the package default. No configuration, no registration — just convention.&lt;/p&gt;

&lt;p&gt;The discovery service also collects metadata from each block — label, icon, description, search keywords, category, sort priority — which powers the enhanced UI in the next layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3: Composable Block Traits
&lt;/h2&gt;

&lt;p&gt;Once you have more than a few blocks, you'll notice they share a lot of form fields — button styles, background colors, spacing, animations. Copy-pasting across blocks is a maintenance nightmare. Traits solve this:&lt;/p&gt;

&lt;p&gt;`&amp;lt;?php&lt;br&gt;
class CallToActionBlock extends RichContentCustomBlock&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
    use HasBlockMetadata;      // Category, icon, description, keywords&lt;br&gt;
    use HasBlockIdentifiers;   // Anchor IDs, CSS classes&lt;br&gt;
    use HasContentWidth;       // Per-block width control&lt;br&gt;
    use HasStylingOptions;     // Button variants, colors, spacing&lt;br&gt;
    use HasAnimationOptions;   // Scroll-triggered entrance effects&lt;br&gt;
}`&lt;br&gt;
Each trait provides both &lt;strong&gt;form schema sections&lt;/strong&gt; (for the editor modal) and &lt;strong&gt;rendering helpers&lt;/strong&gt; (for the frontend). Adding a new block becomes mostly about defining its unique content fields — the styling, animation, and metadata layers come free.&lt;/p&gt;

&lt;p&gt;If you're using a component library like &lt;a href="https://daisyui.com" rel="noopener noreferrer"&gt;daisyUI&lt;/a&gt;, you can map styling options directly to its semantic classes btn-primary, bg-base-200). This gives you theme switching for free — every block re-skins automatically when the theme changes.&lt;/p&gt;

&lt;p&gt;Please read the rest of this article here &lt;a href="https://tallcms.com/blog/extend-filament-v5-rich-editor-block-editor" rel="noopener noreferrer"&gt;https://tallcms.com/blog/extend-filament-v5-rich-editor-block-editor&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>filament</category>
      <category>laravel</category>
    </item>
    <item>
      <title>The SEO-era CMS is dead. Here's what comes next.</title>
      <dc:creator>BuildWithTall</dc:creator>
      <pubDate>Mon, 06 Apr 2026 15:24:22 +0000</pubDate>
      <link>https://forem.com/tallcms/the-seo-era-cms-is-dead-heres-what-comes-next-2jk6</link>
      <guid>https://forem.com/tallcms/the-seo-era-cms-is-dead-heres-what-comes-next-2jk6</guid>
      <description>&lt;p&gt;&lt;strong&gt;Search visibility still matters. But the next advantage is making content structured, attributable, and maintainable.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;WordPress became the dominant CMS by solving a real problem: making it easy to publish crawlable, keyword-targetable content. Install Yoast, fill in the meta description, hit publish. Google does the rest. That playbook still works — but it's no longer sufficient.&lt;/p&gt;

&lt;p&gt;When someone asks ChatGPT, Perplexity, or Claude a question, the answer isn't a list of ten blue links. It's a synthesised response drawn from sources that were readable, attributable, and trustworthy. Your content either makes it into that synthesis — or it doesn't.&lt;/p&gt;

&lt;p&gt;The right question isn't "how do we optimise for AI?" That framing chases the wrong things. The better question is: &lt;strong&gt;what does it mean for content to be genuinely legible in 2025?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What "legible" actually means
&lt;/h2&gt;

&lt;p&gt;Legibility isn't a new concept. It's what good semantic HTML has always been about — content that's meaningful to the medium it travels through, not just to the human reading it in a browser.&lt;/p&gt;

&lt;p&gt;In the SEO era, legibility meant clean URLs, descriptive title tags, heading hierarchy, and internal links. These still matter. But AI systems extract meaning differently than crawlers do. They look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured data&lt;/strong&gt; — not just as an SEO signal, but as a machine-readable contract about what this content actually is&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attributability&lt;/strong&gt; — who wrote this, when, and what makes them credible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extractability&lt;/strong&gt; — does a paragraph answer a specific question, or does it meander?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Freshness signals&lt;/strong&gt; — when was this last verified? Is it still accurate?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A CMS that makes these easy to produce by default — not via plugins bolted on afterward — has a structural advantage in the current landscape.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to be careful about
&lt;/h2&gt;

&lt;p&gt;Before going further, some of the "AI-optimised CMS" conversation is moving faster than the evidence warrants. A few claims worth scrutinising:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"llms.txt is the new robots.txt"&lt;/strong&gt; is an interesting emerging convention, but not yet a stable enough foundation to build a product strategy around. Worth supporting if it matures. Not a core thesis today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"AI tools consume content via APIs, not browsers"&lt;/strong&gt; is overstated. Many AI systems still rely on crawlable, rendered HTML. API-first is genuinely valuable — but clean public HTML remains equally critical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Structured content will be cited by AI more reliably"&lt;/strong&gt; involves factors well outside any CMS's control. The honest version: structured, well-attributed content is more extractable and more likely to survive summarisation intact. That's still a meaningful claim — but it's not a guarantee.&lt;/p&gt;

&lt;p&gt;These cautions matter because the worst version of this thesis is a CMS that chases AI hype instead of building something durable. The best version focuses on what has always mattered: clear, structured, trustworthy content. AI legibility is a consequence of doing those things well, not a separate goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content maintenance is the missed layer
&lt;/h2&gt;

&lt;p&gt;Almost every CMS treats content creation as the primary workflow. Content maintenance — reviewing, updating, verifying — is an afterthought.&lt;/p&gt;

&lt;p&gt;AI systems are exposing this gap. Stale content isn't just an SEO liability anymore. It's a trust signal. A well-maintained corpus that reflects current reality is more useful as a source than a site that published 400 posts in 2021 and stopped updating.&lt;/p&gt;

&lt;p&gt;A CMS that surfaces stale content proactively — "this article was last reviewed 18 months ago, here are five that haven't been touched in two years" — is doing something valuable that almost nobody has built well yet.&lt;/p&gt;

&lt;p&gt;The winning CMS in this era makes content maintenance as easy as content creation. That's a smaller, more tractable problem than "building an AI CMS" — and a more durable one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for TallCMS
&lt;/h2&gt;

&lt;p&gt;We're building TallCMS around the idea that content should be legible to humans, search engines, and AI systems alike. Concretely, that means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema-first content types.&lt;/strong&gt; When you add an FAQ block, a how-to guide, or a product page, the appropriate structured data is generated automatically. Schema.org markup is a primitive, not a plugin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First-class attribution and review metadata.&lt;/strong&gt; Author credentialing, last-reviewed dates, and expert attribution are real fields in the editor — not custom meta buried in settings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance workflows over AI gimmicks.&lt;/strong&gt; Stale content detection, review reminders, and freshness indicators built into the editorial experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TallCMS's block system already stores content as typed, structured data — not WYSIWYG blobs. The leap from structured blocks to structured data output is small. That's a genuine architectural advantage, not a marketing claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we're deliberately not doing
&lt;/h2&gt;

&lt;p&gt;Saying no is as important as saying yes. Here's what we're choosing not to build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI content generation in the editor.&lt;/strong&gt; We're not adding a "write this for me" button. AI-assisted quality — flagging thin content, suggesting structure — is useful. AI-replacing-authorship is not a CMS feature, it's a crutch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headless-only architecture.&lt;/strong&gt; TallCMS is full-stack by default: admin panel, frontend, themes. The API is strong and well-documented for when you need it. But we're not abandoning the integrated experience to chase a trend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chasing llms.txt as a centrepiece.&lt;/strong&gt; We'll support it if the convention stabilises. We're not building our product story around it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Promising AI citeability.&lt;/strong&gt; We can make content more extractable, more structured, and more trustworthy. We can't control what any AI system decides to surface. We won't pretend otherwise.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What to look for in any CMS
&lt;/h2&gt;

&lt;p&gt;Regardless of which CMS you use, here's what matters most in the current landscape:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Schema-first content types&lt;/strong&gt; — structured data as a primitive, not a plugin&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Author and expert attribution&lt;/strong&gt; — first-class fields, not custom meta&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Last-reviewed / last-verified metadata&lt;/strong&gt; — surfaced to editors, not just stored&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FAQ and Q&amp;amp;A blocks with clean semantic output&lt;/strong&gt; — automatic Schema markup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean API output&lt;/strong&gt; — headless-capable without being headless-only&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stale content detection&lt;/strong&gt; — proactive, not on-demand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;llms.txt generation&lt;/strong&gt; — a nice extra, not the centrepiece&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;WordPress won because it made publishing accessible. The next era rewards something different: making content genuinely trustworthy and extractable at every level of the stack.&lt;/p&gt;

&lt;p&gt;The CMS that wins isn't the one that bolts on AI features. It's the one that makes structured, well-attributed, well-maintained content the path of least resistance.&lt;/p&gt;

&lt;p&gt;Built for how content gets discovered now — not how it got indexed then.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>cms</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Why I Built My Own CMS (Again) — This Time with Laravel + Filament</title>
      <dc:creator>BuildWithTall</dc:creator>
      <pubDate>Sat, 04 Apr 2026 14:35:53 +0000</pubDate>
      <link>https://forem.com/tallcms/why-i-built-my-own-cms-again-this-time-with-laravel-filament-2422</link>
      <guid>https://forem.com/tallcms/why-i-built-my-own-cms-again-this-time-with-laravel-filament-2422</guid>
      <description>&lt;p&gt;Most developers don’t wake up thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“You know what the world needs? Another CMS.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And yet… here we are.&lt;/p&gt;

&lt;p&gt;The Problem with Existing CMS (From a Developer’s POV)&lt;/p&gt;

&lt;p&gt;I’ve worked with a lot of CMS platforms over the years — from WordPress to headless setups.&lt;/p&gt;

&lt;p&gt;They all work… until you try to do something slightly different.&lt;/p&gt;

&lt;p&gt;That’s when things get messy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You fight plugins instead of building features&lt;/li&gt;
&lt;li&gt;You bend your architecture around the CMS&lt;/li&gt;
&lt;li&gt;You inherit years of legacy decisions you didn’t sign up for&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At some point, I realized:&lt;/p&gt;

&lt;p&gt;I wasn’t building products — I was working around the CMS.&lt;/p&gt;

&lt;p&gt;The “What If” Question&lt;/p&gt;

&lt;p&gt;So I started asking:&lt;/p&gt;

&lt;p&gt;What if a CMS was built the way we build modern Laravel apps today?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not constrained by legacy.&lt;/li&gt;
&lt;li&gt;Not pretending to be everything for everyone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean architecture&lt;/li&gt;
&lt;li&gt;Developer-first&lt;/li&gt;
&lt;li&gt;Extensible by design&lt;/li&gt;
&lt;li&gt;Actually enjoyable to work with&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why &lt;strong&gt;Laravel&lt;/strong&gt; + &lt;strong&gt;Filament&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;If you’re in the Laravel ecosystem, this part will make sense.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Laravel&lt;/strong&gt; gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structure without being restrictive&lt;/li&gt;
&lt;li&gt;A clean service layer&lt;/li&gt;
&lt;li&gt;First-class DX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Filament&lt;/strong&gt; gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A powerful admin panel out of the box&lt;/li&gt;
&lt;li&gt;Forms, tables, actions — already solved&lt;/li&gt;
&lt;li&gt;A consistent UI layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, they solve 80% of what a CMS needs.&lt;/p&gt;

&lt;p&gt;The remaining 20%?&lt;br&gt;
That’s where things get interesting.&lt;/p&gt;

&lt;p&gt;The Hard Parts (That No One Talks About)&lt;/p&gt;

&lt;p&gt;Building a CMS isn’t about CRUD.&lt;/p&gt;

&lt;p&gt;It’s about abstractions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Content as Blocks (Not Fields)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What I wanted:&lt;/p&gt;

&lt;p&gt;Flexible, composable content blocks&lt;/p&gt;

&lt;p&gt;But that introduces problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do you store them?&lt;/li&gt;
&lt;li&gt;How do you version them?&lt;/li&gt;
&lt;li&gt;How do you render them cleanly?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Editor Experience vs Developer Control&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There’s always tension between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Giving non-tech users flexibility&lt;/li&gt;
&lt;li&gt;Keeping developers sane&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Too flexible → chaos&lt;br&gt;
Too strict → unusable&lt;/p&gt;

&lt;p&gt;Finding that balance is harder than it sounds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Plugin Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I didn’t want:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Just hack it into the codebase”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wanted:&lt;/p&gt;

&lt;p&gt;A system where features can be dropped in cleanly&lt;/p&gt;

&lt;p&gt;Which means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discovery&lt;/li&gt;
&lt;li&gt;Registration&lt;/li&gt;
&lt;li&gt;Isolation&lt;/li&gt;
&lt;li&gt;Extensibility points&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I ended up building TallCMS, an open-source CMS powered by Laravel + Filament.&lt;/p&gt;

&lt;p&gt;Current features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Block-based page builder&lt;/li&gt;
&lt;li&gt;Pages &amp;amp; posts&lt;/li&gt;
&lt;li&gt;Publishing workflow (draft → scheduled → published)&lt;/li&gt;
&lt;li&gt;Revision history&lt;/li&gt;
&lt;li&gt;Media library&lt;/li&gt;
&lt;li&gt;Menu builder&lt;/li&gt;
&lt;li&gt;Plugin architecture &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still early. Still evolving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unexpected Lessons&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Frameworks Matter More Than You Think&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Working within Laravel + Filament constraints actually made things better.&lt;/p&gt;

&lt;p&gt;It forced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistency&lt;/li&gt;
&lt;li&gt;Reusability&lt;/li&gt;
&lt;li&gt;Less over-engineering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Agentic Coding Is Powerful… But Dangerous&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ve been experimenting with AI-assisted development.&lt;/p&gt;

&lt;p&gt;What I learned:&lt;/p&gt;

&lt;p&gt;Without structure → chaos&lt;br&gt;
With a clear plan → massive acceleration&lt;/p&gt;

&lt;p&gt;I now always:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. Generate a plan&lt;/li&gt;
&lt;li&gt;2. Review it&lt;/li&gt;
&lt;li&gt;3. Execute in controlled steps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. CMS Is a Systems Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Build pages”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Design a system that lets others build anything”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That shift changes everything.&lt;/p&gt;

&lt;p&gt;Where This Is Going&lt;/p&gt;

&lt;p&gt;I’m still exploring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better block composition&lt;/li&gt;
&lt;li&gt;AI-assisted content creation&lt;/li&gt;
&lt;li&gt;Multi-site architecture&lt;/li&gt;
&lt;li&gt;Developer experience improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Curious What Others Think&lt;/p&gt;

&lt;p&gt;If you’ve ever:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built your own CMS&lt;/li&gt;
&lt;li&gt;Fought with one&lt;/li&gt;
&lt;li&gt;Or are happily using one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’d love to hear:&lt;/p&gt;

&lt;p&gt;What’s the one thing your CMS does really well — or really badly?&lt;/p&gt;

&lt;p&gt;Closing&lt;/p&gt;

&lt;p&gt;I’m building this as an open-source project called TallCMS.&lt;/p&gt;

&lt;p&gt;Not trying to replace everything.&lt;/p&gt;

&lt;p&gt;Just trying to make something that feels right for modern Laravel developers.&lt;/p&gt;

&lt;p&gt;If you’re curious, happy to share more.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>filament</category>
      <category>claude</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
