SEO · Open source
RankForge Lite
Adds meta, Open Graph and X fields to any Filament resource, scores the content for SEO and readability as you type, and previews the Google result before you publish.
What it solves
In a Filament admin, SEO usually means a few loose text inputs for title and description.
Editors cannot see how the page will look in Google or on social networks, nobody knows whether the content is any good for search until after it ships, and every project hand-writes the same head tags again.
Add the HasSeo trait to a model and SeoFields to its form. The SEO fields are saved to one rankforge_meta row per record, and an analysis panel inside the form re-runs the checks on each live update, then shows an overall score, SEO and readability sub-scores, the check results, a Google preview and social cards. Scores are stored on every save, so a table column can show and sort them, and one Blade component renders the head tags on your site. An independent plugin, not made or endorsed by Filament.
- Live SEO and readability score from 22 checks: 14 SEO checks and 8 readability checks, each run only when it applies to the content
- Analysis in 6 languages (English, Spanish, French, German, Arabic, Urdu), chosen per record; field labels and panel headings are translated too, check messages are in English
- Google result preview with a desktop and mobile switch
- Facebook, X and LinkedIn card previews built from the Open Graph fields
- Sortable score column for your tables, coloured by configurable thresholds
- One Blade component (or the @rankforge directive) for title, description, robots, canonical, hreflang, Open Graph and X tags
- Focus keyphrase, meta title and description, canonical URL, robots and Open Graph fields on any resource
Install
Requires PHP 8.3+, Filament 4 and 5, and Laravel 12 and 13.
composer require amjadiqbal/filament-rankforge-lite
Usage
use AmjadIqbal\FilamentRankForge\Concerns\HasSeo;
use AmjadIqbal\FilamentRankForge\Filament\Forms\SeoFields;
class Post extends Model
{
use HasSeo;
}
// PostResource
public static function form(Schema $schema): Schema
{
return $schema->components([
TextInput::make('title')->live(debounce: 600),
TextInput::make('slug')->live(debounce: 600),
RichEditor::make('content')->live(debounce: 600),
SeoFields::make(contentField: 'content', slugField: 'slug'),
]);
}
Setup
Require the package and run the migrations. They create one table, rankforge_meta, with one row per record. There is no panel plugin to register and no assets to build: the analysis panel carries its own scoped styles, so it renders in a stock panel without a custom theme. By default the trait reads title (or name), slug, content (or body) and description (or excerpt); override getSeoTitle(), getSeoSlug(), getSeoContentHtml(), getSeoDescription() or getSeoCanonicalUrl() on the model if your columns are named differently.
- 1 composer require amjadiqbal/filament-rankforge-lite
- 2 php artisan migrate
- 3 Add use HasSeo; to the model and SeoFields::make() to the resource form.
- 4 Optional: php artisan vendor:publish --tag=rankforge-config
The analysis
SeoFields is a collapsible section bound to the rankforgeMeta relationship. Its SEO fields are live, and the panel recomputes on each form update, so mark your own title, slug and content fields ->live() too if the score should follow them as you type. A RichEditor that stores JSON is rendered to HTML before it is checked. Without a meta title, the panel uses the form's title or name field.
- SEO checks: title length and keyphrase, meta description length and keyphrase, slug keyphrase and length, keyphrase in the first paragraph and subheadings, keyphrase density, content length, internal and outbound links, image alt text, and a depth check that runs only for content marked cornerstone.
- Readability checks: Flesch reading ease, sentence length, paragraph length, passive voice, transition words, subheading distribution, consecutive sentence starts and word complexity.
- Keyphrase checks run only when a focus keyphrase is set. For Arabic and Urdu, Flesch reading ease and word complexity are skipped and passive voice is not detected.
- Related keyphrases (up to 4) are saved with the record but are not scored in the panel or the column; SeoAnalyzer::analyzer()->analyzeKeyphrases($post) runs the keyphrase checks for them in code.
- In config, analysis.disabled_checks turns checks off by id, weights_override changes their weight, and keyword_density_range and content_length_tiers move the thresholds.
Score column
SeoScoreColumn::make() returns a Filament TextColumn showing the stored overall score, sortable through a subquery on rankforge_meta. It is solid coloured text in the panel's danger, warning or success colour: below 40, below 70, and 70 or more by default, set in score_thresholds. Records with no score show a dash. Pass seo_score or readability_score as the second argument to show a sub-score instead (the column label stays SEO Score). Scores are re-calculated whenever the record or its SEO row is saved, including saves made outside Filament, so eager-load the relation and the column costs no extra queries per row.
use AmjadIqbal\FilamentRankForge\Filament\Tables\SeoScoreColumn;
->columns([
TextColumn::make('title'),
SeoScoreColumn::make(),
])
->modifyQueryUsing(fn ($query) => $query->with('rankforgeMeta'))
RankForge
RankForge Lite is the free edition of RankForge, a larger paid SEO suite for Filament at rankforge.amjad.com.pk. The analysis panel shows a one-line link to it, which lite.show_upgrade_link => false hides.
Hire me
Need this built into your app, or something custom?
I wrote RankForge Lite. I can wire it into your Filament panel, extend it for your case, or build the feature around it. Tell me what you need and you'll get a plan and a quote, free.
Support
Found a bug or want a feature? Open an issue on GitHub. For help adding it to your own project, use the quote form above.