FilaRank Pro plugin screenshot
Dark mode ready
Multilingual support
Supports v5.x

FilaRank Pro by Usama Muneer

Community

SEO toolkit for Filament with live scoring, readability analysis, SERP preview, head tag rendering, redirect management, site-wide health reports and many more.

Tags: Developer Tool Kit Widget
Supported versions:
5.x 4.x
Third-party plugin. This is built by the community, not the Filament team. Filament does not review, endorse, or vet the security of plugins outside the filament/ namespace. Review the source and install at your own risk. Found malware or an unresolved security issue the author won't address? Report it .
Usama Muneer avatar Author: Usama Muneer

Documentation

#Documentation Index

Fetch the complete documentation index at: https://docs.filarank.com/llms.txt Use this file to discover all available pages before exploring further.

SEO toolkit for Filament v5: live scoring, GEO/AEO, AI fixes, sitemap indexes, robots.txt, IndexNow, and head tags. Single-file overview for the Filament plugin directory.

FilaRank live SEO scoring in Filament

SEO toolkit for Filament v5: live content scoring, readability and GEO/AEO analysis, SERP preview, head-tag rendering, sitemap indexes, robots.txt / llms.txt, IndexNow, redirect management, AI fix suggestions, keyword research, rank tracking, and site-wide health reports.

#Features

  • 26 analysis checks across three score groups:
    • SEO (13), title length, keyword in title / meta description / slug / first paragraph / subheadings, keyword density with stuffing detection, content length, cornerstone depth, internal links, outbound links, image alt text.
    • Readability (7), Flesch Reading Ease, sentence length distribution, paragraph length, passive voice, transition words, subheading distribution, repeated consecutive sentence openings.
    • GEO / AEO (6), direct-answer blocks, FAQ structure, entity clarity, citation signals, snippet-length meta, structured-data readiness.
  • Live analysis in the form, results re-render as the editor types (debounced), with traffic-light indicators and an overall 0–100 score.
  • AI Fix, one-click title / meta / slug suggestions with a before/after diff. Bring your own OpenAI, Gemini, Anthropic, Mistral, or DeepSeek key.
  • Google SERP snippet preview plus Facebook and X social previews, right where the editor needs it.
  • SeoScoreColumn, a sortable red/amber/green badge for any Filament table.
  • Persisted scores, recalculated whenever the parent model is saved, so table badges never need a live re-analysis.
  • <x-filarank::tags />, one component that renders <title>, meta description, robots, canonical, Open Graph, Twitter Cards, hreflang, and multi-type JSON-LD (@graph) with site-wide fallbacks.
  • <x-filarank::breadcrumbs />, a visible trail that matches BreadcrumbList JSON-LD.
  • XML sitemap index at /sitemap.xml (per-model changefreq/priority, image sitemap, HTML sitemap). /filarank-sitemap.xml 301s to the new path.
  • Crawl files, a panel editor for robots.txt (with revisions and AI-bot presets), llms.txt / llms-full.txt, and IndexNow (key file + submit on save).
  • Admin dashboards, SEO Overview, SEO Health, Keyword Research, Rank Tracker, Backlinks, Competitors, Redirects, SEO Files, Instant Indexing, Bulk SEO, Setup wizard, and Settings. Every page is individually toggleable.
  • Framework-free analysis engine, src/Analysis and src/Support have zero Laravel dependencies, so the engine is trivially unit-testable and reusable outside Filament.
  • Interactive installer, php artisan filarank:install publishes assets, migrates, and wires the trait, form section, and score column into the models and resources you pick, with a --dry-run mode.
  • Fully translatable, publishable views, configurable checks.

New in v3.0: crawl files, sitemap indexes, title templates, social overrides, an extensible schema registry, bulk SEO, IndexNow, and FR/DE/ES readability packs. See Upgrade to v3 if you already have FilaRank installed.

#Requirements

  • PHP 8.3+
  • Laravel 11 / 12 / 13
  • Filament v5

Optional: laravel/head (Laravel 13.17+) if you would rather render tags through Laravel's first-party @head directive. See Using Laravel Head.

#Installation

composer require usamamuneerchaudhary/filarank

php artisan filarank:install

The interactive installer publishes the config and migrations, offers to run migrate, then shows a checklist of your app/Models classes. For each model you pick it will:

  1. add the HasSeo trait (plus import) to the model,
  2. find the matching Filament resource anywhere under app/Filament, and, with your confirmation, append SeoFields::make() to the form and SeoScoreColumn::make() to the table, with imports.

Patching is deliberately conservative: files are only modified when the expected structure is found, every edit is idempotent (safe to re-run), and anything that can't be patched automatically falls back to printed manual instructions. Use --dry-run to preview without writing:

php artisan filarank:install --dry-run

Prefer to wire things up by hand? The manual steps below are exactly what the installer automates:

php artisan vendor:publish --tag=filarank-migrations
php artisan migrate

Optionally publish the config, views, or translations:

php artisan vendor:publish --tag=filarank-config
php artisan vendor:publish --tag=filarank-views
php artisan vendor:publish --tag=filarank-translations

#Register the panel plugin

The admin pages live behind a plugin so you only get the ones you want:

use Usamamuneerchaudhary\FilaRank\FilaRankPlugin;

public function panel(Panel $panel): Panel
{
    return $panel->plugin(FilaRankPlugin::make());
}

Every page defaults to on and can be switched off individually:

FilaRankPlugin::make()
    ->overview()             // SEO Overview dashboard
    ->healthDashboard()      // 404s, cannibalization, orphans, technical audit
    ->keywordResearch()
    ->rankTracker()
    ->backlinks(false)       // hide the pages you don't need
    ->competitors(false)
    ->settings()
    ->redirects()
    ->files()                // robots.txt + llms.txt
    ->indexing()             // IndexNow
    ->bulkEditor()
    ->setupWizard()

#Usage

#1. Prepare your model

use Usamamuneerchaudhary\FilaRank\Concerns\HasSeo;

class Post extends Model
{
    use HasSeo;
}

By default the analyzer reads the model's content and slug attributes and falls back to title for the page title. Override if your columns differ:

public function getSeoContent(): ?string
{
    return $this->body;
}

public function getSeoSlug(): ?string
{
    return $this->permalink;
}

/** Public URL, used by the sitemap, link graph, and technical audit. */
public function getSeoUrl(): ?string
{
    return route('posts.show', $this, absolute: false);
}

#2. Add the SEO section to your Filament form

use Usamamuneerchaudhary\FilaRank\Forms\SeoFields;

public static function form(Schema $schema): Schema
{
    return $schema->components([
        TextInput::make('title'),
        TextInput::make('slug'),
        RichEditor::make('content'),

        SeoFields::make(), // assumes `content` + `slug` fields on this form
    ]);
}

If your body field has a different name, or lives somewhere unusual in the schema tree:

SeoFields::make(contentField: 'body', slugField: 'permalink');

// or take full control of where values are read from and written to:
SeoFields::make(
    getContentUsing: fn (Get $get) => $get('../body'),
    getSlugUsing: fn (Get $get) => $get('../permalink'),
    setSlugUsing: fn (string $value, Set $set) => $set('../permalink', $value),
);

SeoFields::make() binds to the seo relationship, so it must be used on a form with a record model that uses HasSeo.

#3. Show the score in your table

use Usamamuneerchaudhary\FilaRank\Tables\SeoScoreColumn;

public static function table(Table $table): Table
{
    return $table->columns([
        TextColumn::make('title'),
        SeoScoreColumn::make(),
    ]);
}

Scores are recalculated and stored whenever the model is saved (disable with 'persist_score' => false).

#4. Render the tags on your frontend

In your layout's <head>:

<x-filarank::tags :model="$post" />
<x-filarank::breadcrumbs :model="$post" />

Or for static pages without a model:

<x-filarank::tags title="Contact us" description="Get in touch with our team." />

#Using Laravel Head

Laravel 13.17 shipped laravel/head, a first-party way to manage the document <head>. FilaRank works with it, so you don't have to choose between the panel you're editing SEO in and the head management the rest of your app uses.

It stays optional. FilaRank supports Laravel 11 and 12, laravel/head requires 13.17+, so it is a suggest rather than a require. Nothing changes if you don't install it.

composer require laravel/head

Then push the record's stored meta before the view renders, instead of placing the component:

use Usamamuneerchaudhary\FilaRank\SeoHead;

public function show(Post $post): View
{
    SeoHead::for($post);

    return view('posts.show', ['post' => $post]);
}

Static pages take the same overrides as the component:

SeoHead::for(null, title: 'Contact us', description: 'Get in touch with our team.');

Keep Laravel Head's @head directive in your layout and drop <x-filarank::tags />. Render one or the other, never both, or every page gets two titles and two JSON-LD blocks.

Both renderers read the same resolution chain (explicit argument, then the stored SEO row, then your model's fallbacks, then site config) and emit the same title, description, canonical, robots, Open Graph, Twitter card, and JSON-LD graph. Two deliberate differences:

  • Laravel Head normalizes canonical URLs, forcing https and giving a root path its trailing slash. The component emits whatever you stored, verbatim.
  • FilaRank's JSON-LD is scrubbed recursively before handover, because Laravel Head rejects null and empty-string values at any depth. This mostly matters if your model's getProductSchema() returns partially-filled nested nodes.

SeoHead::for() throws if laravel/head isn't installed. Guard with SeoHead::isAvailable() if you ship code that has to run either way.

#5. Analyze anything programmatically

use Usamamuneerchaudhary\FilaRank\SeoAnalyzer;

$report = SeoAnalyzer::analyze([
    'title' => 'Coffee Beans Guide',
    'description' => '…',
    'focus_keyword' => 'coffee beans',
    'slug' => 'coffee-beans',
    'content' => $html,
]);

$report->score();            // 0–100 or null
$report->seoScore();
$report->readabilityScore();
$report->geoScore();
$report->rating();           // Status::Good | Ok | Bad
$report->results;            // list of CheckResult

Or against a model directly: SeoAnalyzer::analyzeModel($post).

#AI features

FilaRank is bring-your-own-key. Add a key under FilaRank SEO → SEO Settings (stored encrypted via Crypt), or fall back to env: OPENAI_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY.

Feature Where What it does
Fix with AI SEO section header, per record Suggests title, meta description, and slug, shown as a before/after diff before anything is written
Audit fix plan SEO Health → Technical audit Turns an audit issue into a numbered remediation plan
Keyword enrichment Keyword Research Adds intent, difficulty, and volume estimates to a seed keyword
Competitor gaps Competitors Suggests keywords a competitor likely targets that you don't
Backlink toxicity Backlinks Scores a link 0–100 for spamminess

Every call is logged to filarank_ai_logs with provider, model, token counts, and estimated cost, surfaced as a monthly spend figure on the Overview dashboard. Nothing runs unless a key is configured, AI actions hide themselves when AiService::isConfigured() is false.

#Choosing a model

The model dropdown in SEO Settings is built from config, and that list is the allowlist, add a model and it becomes selectable, with no code change and nothing hardcoded on the package side:

'ai' => [
    'providers' => [
        'openai' => [
            'api_key' => env('OPENAI_API_KEY'),
            'models' => ['gpt-4o-mini', 'gpt-4o', 'your-new-model'],
            'rates' => [
                'default' => ['in' => 0.15, 'out' => 0.60],
                'gpt-4o' => ['in' => 2.50, 'out' => 10.00],
            ],
        ],
    ],
],

The first entry is the default when nothing has been chosen, so put your preferred model first. A saved selection is validated against the list on every read, so removing a model from config quietly falls back to that first entry rather than erroring.

rates are USD per 1M tokens and drive the spend estimate. Key a rate by model name to price it exactly; anything without its own entry uses default. Getting this wrong only skews the dashboard figure, never a request.

Two caveats when adding a model. Requests are built per provider, not per model, the OpenAI provider sends temperature and a JSON response format, so reasoning-style models that reject a custom temperature will fail with a 400. And because publishing the config replaces the package's ai block wholesale (mergeConfigFrom merges only top-level keys), a published copy won't inherit providers added in later versions. Either way, Test AI connection in Settings does a real round trip and surfaces the problem immediately.

To use a provider directly, or in tests:

use Usamamuneerchaudhary\FilaRank\Ai\AiService;
use Usamamuneerchaudhary\FilaRank\Ai\Providers\FakeProvider;

$suggestion = app(AiService::class)->suggestSeoFix([
    'title' => $post->seo->title,
    'description' => $post->seo->description,
    'focus_keyword' => 'coffee beans',
    'slug' => $post->slug,
    'content' => $post->content,
]);

// Deterministic output for tests (local/testing environments only):
(new AiService)->useProvider(new FakeProvider)->suggestSeoFix([...]);

All provider failures are wrapped in AiRequestException with credentials stripped out, so a bad key or rate limit never leaks a secret into your logs or a notification.

#Sitemap

Enable the sitemap in SEO Settings; while it's off, the route returns 404 rather than an empty document. Entries are built from your scanned models, skipping records flagged noindex or without a public URL.

'sitemap' => [
    'enabled' => true,
    'path' => 'sitemap.xml',
    'legacy_path' => 'filarank-sitemap.xml',
    'index' => true,
    'images' => true,
    'html' => true,
    'changefreq' => 'weekly',
    'priority' => 0.5,
    'cache_seconds' => 3600,

    'ping' => false,
    'ping_endpoints' => [
        'https://www.bing.com/ping?sitemap={sitemap}',
    ],
],

With pinging enabled, endpoints are called after a record is saved. Requests are deferred until after the response, so a slow or dead endpoint never delays a content save. The sitemap placeholder in each ping URL is replaced with the URL-encoded sitemap URL. Google retired its ping endpoint in 2023, so the list is deliberately easy to repoint.

#Technical audit

SEO Health → Technical audit crawls the public URL of every record with SEO meta and reports missing titles, meta descriptions, canonicals, OG images, and focus keywords, plus broken status codes, absent JSON-LD, and images without alt text.

The crawler only fetches URLs on your own app.url host. Extra hosts must be allow-listed and must resolve to public addresses, which stops a careless getSeoUrl() from pointing the crawler at internal services or cloud metadata endpoints. Blocked URLs are recorded as an unsafe_url issue so the meta checks still run and you can see why the fetch was skipped.

'audit' => [
    'allowed_hosts' => ['docs.example.com'],
    'max_pages' => 500,
    'delay_ms' => 0,       // throttle between requests
],

Runs are bounded by max_pages and atomic: issues are collected in memory and swapped in one transaction, so a crawl that dies halfway leaves your existing issue list intact.

#Keyword research & rank tracking

Research pulls Google Autocomplete suggestions plus modifier expansions for a seed keyword, saves them as untracked rows, and lets you promote the ones you care about to tracked keywords. The table supports search, sorting, filtering by tracked state, and bulk track/dismiss.

use Usamamuneerchaudhary\FilaRank\Keywords\KeywordStore;

$store = app(KeywordStore::class);
$store->saveResearched(['coffee beans', 'best coffee beans']);
$store->track('coffee beans');       // never clears existing enrichment
$store->dismiss([$id]);              // untracked rows only; tracked keywords are safe

Tracked keywords get daily position checks. SerpAPI is the supported provider, add the key in SEO Settings:

php artisan filarank:check-ranks

The command is scheduled automatically (rank_tracker.schedule_at, default 03:00) and exits cleanly with a warning when no key is configured, so an unconfigured install doesn't fail every night. Scraping Google breaks their terms of service and stops working without warning, so it's disabled unless you opt in with FILARANK_ALLOW_SCRAPE=true; until then the option is hidden from Settings.

Research rows accumulate, so prune them when you're done:

php artisan filarank:prune-keywords --days=30          # dry run, lists what would go
php artisan filarank:prune-keywords --days=30 --force  # actually delete

Tracked keywords are never pruned, they hold your rank history.

#Backlinks & competitors

Both are deliberately simple: you own the data, FilaRank enriches it.

Backlinks imports a CSV export from Ahrefs, Semrush, or Search Console. Headers are matched flexibly (source_url/url/referring page, dr/domain_rating, and so on), and rows are matched on source + target URL, so re-importing a refreshed export updates rows instead of duplicating them. Optional AI toxicity scoring runs 25 unscored links at a time.

Competitors is a domain list with an "Analyse gap" action that asks the model which keywords a competitor likely targets that you don't. These are informed estimates from the domain and your own keyword list, not crawled ranking data, treat them as research prompts, not measurements.

#Configuration

See config/filarank.php for site-wide defaults (site name, title separator, default description / share image, Twitter handle), tag-rendering toggles, schema types, disabled checks, and score persistence.

Disable individual checks:

'analysis' => [
    'disabled_checks' => ['outbound-links', 'passive-voice'],
],

Choose which JSON-LD nodes are emitted (also toggleable from SEO Settings):

'render' => [
    'schema_types' => [
        'organization' => true,
        'website' => true,
        'breadcrumb' => true,
        'article' => true,
        'faq' => true,
        'product' => false,
    ],
],

List the models FilaRank should scan for the sitemap, audit, cannibalization, and link graph:

'scanned_models' => [
    \App\Models\Post::class => ['label' => 'title'],
    \App\Models\Page::class => ['label' => 'name'],
],

Leave it empty and FilaRank falls back to any model that already has SEO meta rows, so the dashboards aren't blank on a fresh install.

#Security notes

  • API keys are encrypted at rest and shown masked in the UI. Paste a new value to replace one; leave the field blank to keep it.
  • Provider errors pass through Support\Redactor before being logged or displayed, stripping query-string credentials, bearer tokens, and known key formats.
  • The audit crawler is host-restricted (see Technical audit) and refuses redirects to untrusted hosts mid-request.
  • The fake AI provider is refused outside local and testing, so a misconfigured production panel can't silently serve canned copy.
  • Google scraping is off by default and must be enabled explicitly in config.

#Writing your own check

Implement the Check contract and register it:

use Usamamuneerchaudhary\FilaRank\Analysis\Contracts\Check;
use Usamamuneerchaudhary\FilaRank\Analysis\{CheckResult, ContentContext, Status};

final class NoClickbaitTitle implements Check
{
    public function id(): string { return 'no-clickbait'; }
    public function group(): string { return 'seo'; }

    public function isApplicable(ContentContext $context): bool
    {
        return filled($context->title);
    }

    public function run(ContentContext $context): CheckResult
    {
        $clickbait = str_contains(mb_strtolower($context->title), 'you won\'t believe');

        return new CheckResult(
            $this->id(),
            $this->group(),
            $clickbait ? Status::Bad : Status::Good,
            $clickbait ? 'Avoid clickbait phrasing in titles.' : 'Title looks trustworthy.',
        );
    }
}
$report = SeoAnalyzer::analyzer()
    ->withCheck(new NoClickbaitTitle())
    ->analyze($context);

Groups are seo, readability, and geo; anything else is ignored by the score panels.

#Cross-record reports

All the cross-record logic lives in framework-free, unit-tested engines (src/Cannibalization, src/Linking, src/Redirects, src/Language, src/Keyphrase).

#Redirect manager (404 capture + one-click 301s)

Register the middleware in bootstrap/app.php:

use Usamamuneerchaudhary\FilaRank\Http\Middleware\HandleRedirects;

->withMiddleware(function (Middleware $middleware) {
    $middleware->web(append: [HandleRedirects::class]);
})

The middleware serves matching redirects (with normalisation, chain-following A→B→C, and loop protection) and logs every 404. Logged 404s appear on the SEO Health page with a one-click "Create redirect" link that pre-fills the source path. Rules are cached for an hour and the cache is busted automatically on create/edit/delete.

Slug changes on published records create a 301 automatically. Reverting a slug clears the reverse rule, so renaming back and forth can't leave a redirect loop behind. Configure with:

'redirects' => [
    'auto_on_slug_change' => true,
    'only_when_published' => true,
],

#Internal linking suggestions & orphaned content

The SEO Health dashboard reports orphaned content (pages nothing links to) and under-linked cornerstones. The link graph compares URLs by normalised path, so absolute and relative links to the same page match. Programmatic access:

$graph = app(\Usamamuneerchaudhary\FilaRank\Reports\SeoReports::class)->linkGraph();
$graph->orphans();
$graph->suggestionsFor($record);       // keyphrase-based, cornerstone-boosted
$graph->underlinkedCornerstones(3);

#Keyword cannibalization warnings

Any keyphrase targeted by more than one record is flagged, sorted most-contested first. Keywords are normalised (case- and slug-insensitive), so "Coffee Beans", "coffee beans", and "coffee-beans" collide. Includes each record's primary and related keyphrases.

#Cornerstone content flagging

A Cornerstone content toggle in the SEO section marks your most important pages. Cornerstone records are held to a higher bar by the cornerstone-depth check (≥900 words, properly subheaded) and flagged on the dashboard when too few internal links point to them (config('filarank.cornerstone.min_incoming_links')).

#Multiple focus keywords

A Related keyphrases tags field lets each record target secondary phrases. The primary keyword gets the full analysis; each related keyphrase gets the keyword-specific SEO checks (title / meta / slug / first paragraph / density / subheadings), shown in the analysis panel. Duplicates of the primary are de-duplicated automatically.

use Usamamuneerchaudhary\FilaRank\Keyphrase\KeyphraseAnalyzer;

$results = (new KeyphraseAnalyzer())->analyze($context); // primary first, then related

#Per-language readability packs

Readability heuristics (transition words, passive-voice detection, syllable counting, reading-ease formula) sit behind a LanguagePack interface. English, Dutch, French, German, and Spanish ship in the box. The content-language selector is hidden unless FILARANK_MULTILINGUAL=true. Add your own:

use Usamamuneerchaudhary\FilaRank\Language\LanguageRegistry;

app(LanguageRegistry::class)->register(new FrenchPack());

Extend AbstractLanguagePack and implement transitionWords(), countSyllables(), and the passive-voice word lists, see EnglishPack and DutchPack for the pattern.

#Styling

The panel UI is built from native Filament components plus hand-written filarank-* classes layered on Filament's design tokens, inlined once per process. There is no Tailwind build step and no viteTheme requirement, the plugin looks right in any panel, light or dark, out of the box.

#Testing

composer test              # Pest (Laravel integration, requires composer install)
composer test:standalone   # both engine suites, zero dependencies, just PHP

#Roadmap ideas

  • AI content writer (draft full articles from keyword research)
  • More schema types from the registry
  • Deeper GSC CSV reports beyond the bulk importer

#License

Commercial. One license covers one production project (its development and staging environments included). Purchase at Anystack.

#Support

Email hello@usamamuneer.me.

Full documentation: docs.filarank.com

This documentation is built and hosted on Mintlify, a developer documentation platform.

The author

Usama Muneer avatar Author: Usama Muneer

Coder, Blogger, Tech Speaker & Web Technologies Enthusiast. Passionate about working on open-source Programming languages & Tools while utilizing my Product Development skills.

Plugins
6
Stars
42

From the same author

Spatie Model States Visualizer plugin thumbnail

Spatie Model States Visualizer

State-aware Kanban board and status timeline components for Filament v5. Filament Model States provides a plug and play auto-detection for enums, Spatie Model States, or plain database values. Built to sit on top of: spatie/laravel-model-status > powers the timeline (status history log) spatie/laravel-model-states > powers transition validation and column discovery on the Kanban board (optional but recommended)

Usama Muneer avatar Author: Usama Muneer
73 / 100 package health score out of 100
4 stars
Tag: Developer Tool Tag: Infolist Entry More tags: +1
Dark mode ready No multilingual support
Free
Get it now
Adment plugin thumbnail

Adment

A simple custom ad & Google AdSense manager for Filament v5 with placements, responsive (and GIF/video) creatives, weighted A/B rotation, scheduling windows, geo/device targeting, impression & CTR analytics, obfuscated click tracking, AdSense Auto Ads & ad units, ads.txt management, and an optional public JSON API.

Usama Muneer avatar Author: Usama Muneer
70 / 100 package health score out of 100
1 star
Tag: Analytics Tag: Developer Tool
No dark mode support No multilingual support
Free
Get it now
Command Palette plugin thumbnail

Command Palette

A Spotlight/CMD+K style command palette for quick navigation and actions across Filament panels.

Usama Muneer avatar Author: Usama Muneer
89 / 100 package health score out of 100
4 stars
Tag: Developer Tool Tag: Panels
Dark mode ready No multilingual support
Free
Get it now
Notifier plugin thumbnail

Notifier

A powerful notification system that handles multi-channel notifications with template management, scheduling, and real-time delivery. Built for developers who need enterprise-grade notifications without the complexity.

Usama Muneer avatar Author: Usama Muneer
87 / 100 package health score out of 100
28 stars
Tag: Forms Tag: Tables More tags: +1
Dark mode ready Multilingual support
Free
Get it now