@starstack/strapi-plugin-content-optimizer
v1.0.1
Published
Real-time SEO and readability analysis for Strapi v5. Inline field stats, content validation, and readability checks - live as you type.
Downloads
184
Readme
Content Optimizer - Strapi Plugin
Real-time SEO and readability analysis for Strapi v5. Inline field stats, content validation, and readability checks - all updating live as you type, directly inside the Content Manager.
Features
- Inline field feedback - character count, word count, and a status indicator injected below the title and description fields
- Pixel-accurate truncation warnings - uses the Canvas API to measure rendered width the same way Google does, so truncation warnings are reliable rather than character-count approximations
- Readability analysis - average sentence length, average paragraph length, paragraph density, and longest-sentence callouts
- Content validation - hard errors for missing or undersized content, warnings for long sentences
- Context-aware sidebar panel - switches between title stats, description stats, and full content stats depending on which field is active
- Two-path content extraction - reads Strapi Blocks JSON directly when available; falls back to DOM extraction for unsaved documents
Requirements
| Dependency | Version | | --------------------- | -------- | | Strapi | ^5.0.0 | | React | ^18.0.0 | | @strapi/design-system | ^2.0.0 | | Node.js | ≥18, ≤24 |
Installation
1. Install the package
npm install @starstack/strapi-plugin-content-optimizer2. Enable the plugin
In config/plugins.ts:
export default {
"content-optimizer": {
enabled: true,
},
};Or in config/plugins.js:
module.exports = {
"content-optimizer": {
enabled: true,
},
};3. Rebuild and start
npm run build
npm run developWhere It Appears
Once installed, two UI elements appear inside any Content Manager edit view:
Inline field stats
Injected directly below the Title and Description fields:
142 characters | 23 words | Optimal ●
┌─────────────────────────────────────────┐
│ ✅ Optimal │
└─────────────────────────────────────────┘Right sidebar panel
A Content Statistics panel in the right sidebar that switches context based on the active field:
- Title / Description active → shows character count, word count, reading time
- Content editor active → shows word count (colour-coded), sentence count, paragraph count, and reading time.
How Content Is Extracted
The plugin uses a two-path extraction strategy for Strapi Blocks fields:
Path 1 - Blocks JSON (preferred): Reads the structured JSON from unstable_useContentManagerContext directly. Accurate for saved and unsaved content.
Path 2 - DOM fallback: Walks the live Slate editor DOM when JSON is unavailable. Uses a leaf-block filter to avoid double-counting Slate's nested <li><p> structure.
Content is classified into three buckets:
headingBlocks(h1–h6) - contribute to word count onlyparagraphBlocks(p, blockquote) - used for paragraph metrics and sentence metricslistBlocks(li) - used for word count and sentence metrics, not paragraph count
Field Detection
Fields are located by querying the live DOM in priority order:
Title:
[data-strapi-field="title"] inputinput[name="title"]- Label-text match for "meta title"
Description:
[data-strapi-field="metaDescription"] textarea[data-strapi-field="description"] textareatextarea[name="metaDescription"]textarea[name="description"]- Label-text match for "meta description"
Content editors:
- All
[data-strapi-field]wrappers with acontenteditablechild, excluding known SEO fields - Any remaining
contenteditableelements not under a[data-strapi-field]wrapper (covers dynamic zones)
Known Limitations
- Uses
unstable_useContentManagerContext- this is an internal Strapi API marked unstable. It works across current Strapi 5.x releases but may require an update on future Strapi versions. - Inline stats are injected via DOM manipulation (not React portals) to work reliably across Strapi's CSS grid layout. This means they are not rendered by React and won't appear in server-side renders.
- Field name detection is heuristic-based. Custom field names that don't match the standard patterns (
title,description,metaDescription) may require the Strapidata-strapi-fieldattribute to be present.
License
MIT © Starstack
