B Blengi docs

Operate

SEO surface

Every public route emits a per-page SEO block — title, description, canonical URL, Open Graph card, Twitter card, and JSON-LD structured data — without any per-page work in the React layer. Buyers running a white-label install inherit the same machinery; replace the brand name in /settings/branding and the meta block follows.

What ships out of the box

  • Per-route titles + descriptions. Defined in App\Support\SeoMeta::ROUTE_DEFAULTS. Tokens like {brand} are interpolated at render time so a renamed install never leaks the source product name into the meta block.
  • Canonical URLs. Every page declares its canonical via <link rel="canonical">, anchored on config('app.url') + the route path. Search engines never have to guess which variant is the master.
  • Open Graph + Twitter cards. Shared social previews cover og:type, og:site_name, og:title, og:description, og:url, og:image, plus the twitter:card="summary_large_image" companion. The OG image defaults to public/og-image.png; drop your own in there to override.
  • JSON-LD structured data. Three blocks layer on top of the standard meta:
    • Organization — every page. Brand name, canonical URL, and (when present) the logo.
    • SoftwareApplication — home only. Lets Google's rich-result picker render us as an app with an offer + free-tier price.
    • FAQPage — home only, sourced from the marketing FAQ admins edit at Settings → Marketing. Edits flow through to the structured data automatically — Google's rich-result picker renders Q+A directly under the search snippet for matching queries.
  • /sitemap.xml — every marketing page, every documentation slug, and every published changelog version. Cached for 1 hour at the controller level so a fresh changelog entry shows up within the cache window.
  • /robots.txt — allows public surfaces; disallows /admin, /app, /api/, /settings, and the auth flows; references the sitemap so crawlers find it on first sniff.

Adding SEO to a new route

Two places to touch when adding a new public page:

  1. Register a default in SeoMeta::ROUTE_DEFAULTS with title / description / path. Use {brand} for any place the install's name should appear.
  2. In the controller, pass the SEO payload to Inertia:
    return Inertia::render('your/page', [
        // ... your props
        'seo' => SeoMeta::for('your-route-key'),
    ]);

The Inertia root layout (resources/views/app.blade.php) reads props.seo and emits the meta block automatically. No per-page Blade work needed.

Need to vary the description per row (e.g. a per-version changelog page)? Pass overrides:

'seo' => SeoMeta::for('changelog.show', [
    'title' => "v1.1.0 — what's new in {brand}",
    'description' => "Released " . $entry->released_at_human . " — " . $entry->summary,
])

Customising the Open Graph image

Drop a 1200×630 PNG (or JPG) at public/og-image.png. Buyers running their own install can swap the file directly via SFTP or the deploy host's file manager. The SeoMeta resolver checks the file exists at render time; if it doesn't, the meta block silently omits og:image and twitter:image rather than pointing at a 404.

Sitemap cache

The sitemap is cached for 1 hour under seo:sitemap.xml. To force a refresh after publishing a long-awaited changelog entry:

php artisan tinker --execute 'cache()->forget("seo:sitemap.xml");'

Crawler traffic on a busy install would otherwise be O(crawl rate) database hits; the cache flattens it to one rebuild per hour per node.

Excluded routes

Route prefixWhy excluded
/admin/* Platform-admin surface. Tenant-aware data.
/app/* Workspace dashboard. Auth-required, workspace-scoped.
/api/* API surface — JSON, not for crawlers.
/login, /register, password reset Auth flows — no SEO value, nothing for a crawler to index.
/settings/* Auth-required user / platform settings.

Editable homepage SEO content block

The homepage carries an optional, admin-authored SEO content block rendered below the main content, above the footer — a place to add a keyword-rich, well-structured SEO section without touching template code. Edit it under /settings/marketing → the SEO block tab:

  • Show toggle — off by default, so the homepage is unchanged until you opt in. When off, nothing renders.
  • Heading (optional) — a section title.
  • BodyMarkdown: # / ## / ### for H1/H2/H3, [text](https://…) for links, - for lists, plus bold/italic. Works across all themes (harvest / aurora / prism).

The layout adapts to the Markdown's structure so longer content reads as a native landing section rather than an article pasted below the page:

  • A heading that opens the body (# or ##) is promoted to the section's display heading when the Heading field is empty.
  • Two or more subsections (## headings — or ### when no ## is used) render as a wide, centered section: the heading and any opening paragraphs appear centered up top, and each subsection becomes a card in a responsive grid (two columns, three when the count fills them). Deeper headings stay inside their card.
  • Plain Markdown without subsections keeps a single readable column.

The body is rendered to HTML server-side through SafeMarkdown — the same hardened converter the knowledge base uses. Raw HTML tags and javascript: links are stripped, so the block can't introduce script onto the public homepage even though only super-admins can edit it. The stored value keeps your raw Markdown, so the editor round-trips cleanly.

This is distinct from the per-page meta title / description (still set by SeoMeta): the block adds visible, indexable body content, which is what most "add an SEO section" asks are really after.

The SEO block is translatable per language
Both the block's heading and its full Markdown body are surfaced in Settings → Translations, so you can author a keyword-rich Dutch or German version of the section — the body is one entry you translate as a whole, and the localized Markdown re-renders into the same card layout. The per-route page title, og:title, and twitter:title (all the same SeoMeta title) are translatable there too; the homepage title and description ship with Dutch and German values.

Editable /how-it-works and /integrations pages

The /how-it-works and /integrations marketing pages are admin-editable — no code change to adjust copy or SEO. Edit them under /settings/marketing in the How it works page and Integrations page sections:

  • How it works — the step walkthrough (number, title, duration, description, and bullet points per step) plus the page's meta title and meta description.
  • Integrations — the data-source cards and the roadmap cards (name + tagline, plus an icon name for data sources), plus the page meta. The native integration cards have their own editor.

Both pages fall back to the shipped defaults for any field you leave blank, so an empty meta title keeps the built-in SeoMeta default. The content lives in app_settings.how_it_works_content / integrations_content; the public React pages already read these props, so edits show up immediately on save (after a deploy of the built assets).