Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Alt Seo Laravel Package

alt-design/alt-seo

Alt SEO is a Laravel package for managing SEO metadata on your models and pages. It helps set titles, meta descriptions, Open Graph tags, and canonical URLs so your site stays search-engine friendly with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm the package type first. Despite the "Laravel addon" framing, alt-design/alt-seo is a Statamic addon (repo name Alt-SEO-Addon, author is a Statamic agency). It will not do anything in a plain Laravel app — you need a Statamic site (v3/v4/v5 depending on the release you pin). Verify the statamic/statamic constraint in composer.json before you start.

Minimal install path:

composer require alt-design/alt-seo:1.5.1   # pin exactly; see Gotchas

Statamic auto-discovers addons through composer.json extra.statamic, so no manual provider registration is normally required. Then:

php artisan vendor:publish --tag=alt-seo-config   # verify actual tag in the README
php artisan statamic:stache:clear                 # addon registers blueprints/fieldsets

Where to look first, in this order, so you don't guess at the API:

  1. README.md — install + the Antlers tag/partial it expects you to include.
  2. config/ — defaults, per-collection toggles, whether output is global or opt-in.
  3. src/Tags/ — the Antlers tag class(es); this tells you the exact template variable name (often {{ alt_seo }}, but confirm — do not assume).
  4. resources/views/ — the shipped meta-tag partial. Copy it into your theme rather than editing vendor files. Re-diff this file on every upgrade — v1.5.1 changed canonical and escaping behaviour inside it.
  5. src/Fieldtypes/ or resources/blueprints/ — how SEO fields get injected into entry editing.

First use case: drop the addon's meta partial into your single layout, above </head>:

{{# resources/views/layouts/_meta.antlers.html #}}
{{ alt_seo }}   {{# ← confirm tag name in src/Tags #}}

Load one entry in the browser, View Source, and confirm <title>, <meta name="description">, canonical, and OG tags render. That single page render is your smoke test for everything else. On v1.5.1, additionally check a paginated URL (/blog?page=2) — see the canonical gotcha below.

Implementation Patterns

Layout-partial as the single injection point. Never call the tag in multiple templates. One include in layouts/_meta.antlers.html, included by your base layout, so cascade logic and duplicate-tag prevention stay in one place.

Fieldset-based per-entry editing. The addon's value is the Control Panel experience — SEO fields added to your entry blueprint (seo fieldset with title override, description, OG image, noindex). Pattern: keep the fieldset in resources/fieldsets/seo.yaml and import it into each collection blueprint, so all collections share one schema.

Fallback cascade — and test it deliberately. Typical ordering is entry SEO field → collection-level default → global config default. The failure mode is a blank <title> when all three are empty. Add an explicit terminal fallback in your layout:

<title>{{ seo:title ?? title ?? site:name }}</title>

Social cards belong to the same partial. OG/Twitter tags need an absolute URL for the image (og:image), so pair the addon output with {{ glide:... absolute="true" }} or asset:url plus site:url. Relative og:image URLs are the most common silent failure.

Multisite: localize per site. SEO fields are content, so they live in each site's content tree. Pattern: use the addon's per-site config for defaults (site name suffix, default OG image) and let entry fields override. If you need locale-specific hreflang, verify the addon emits it — if not, hand-roll it in the same partial rather than layering a second SEO package.

Sitemap/robots integration. If the addon ships a sitemap, point robots.txt at it and make sure the noindex field actually excludes entries. Test with curl -s https://site/sitemap.xml | head — a sitemap that lists noindexed entries is worse than none.

Canonical: do not trust the default on v1.5.1+. PR #59 strips query strings from the canonical URL. That's correct for tracking params (?utm_source=) and wrong for pagination (?page=2), filters, and faceted listings. Pattern: build the canonical yourself in your copied partial for any template that paginates:

{{ if paginate:current_page > 1 }}
  <link rel="canonical" href="{{ current_url }}">
{{ else }}
  {{# addon output, or your own URL #}}
{{ /if }}

Decide per-project whether you want filtered views canonicalised to the base URL (usually yes) or self-canonicalised (sometimes). Don't leave it to the addon default.

Programmatic use in Blade. If you render some pages through Blade rather than Antlers, resolve the tag via the Statamic view layer, or fall back to reading the entry's SEO fields directly ($entry->get('seo')) and building tags yourself. Don't mix both approaches on one page.

Testing checklist per release: view-source on a home page, an entry, a paginated listing (/blog?page=2), a filtered listing, a 404, and a multisite alternate. Paginated pages are where canonical/duplicate-title bugs surface — and since v1.5.1 changed exactly that, this is now the primary regression check on upgrade.

Gotchas and Tips

Compatibility is the top risk. 12 stars and 0 dependents means this is essentially a single-maintainer addon. Pin an exact version in composer.json (e.g. "alt-design/alt-seo": "1.5.1"), and keep the published meta partial in your own theme so a breaking tag rename is a one-file diff. Have a plan for forking — it's MIT, so vendoring src/ is trivial if the maintainer stops releasing.

v1.5.1 changed canonical behaviour — audit before deploying. PR #59 strips query strings from the canonical URL. Anything that paginates or filters via query string will now canonicalise to the unfiltered base URL, which deindexes page 2+ and filtered archives. If you upgraded from 1.5.0, diff your rendered canonicals on /blog?page=2 before and after. Either override in your own partial (pattern above) or document the decision.

v1.5.1 escapes entry values in meta tags — watch for double-escaping. PR #58 added escaping of entry values, which is a genuine hardening fix (untrusted content in <title>/og: attributes). But it means: (a) if your Antlers partial also applies | sanitize / | entities / | e, you'll now get &amp;amp;; (b) any editor who legitimately pasted HTML into an SEO description field will now see it rendered literally, not parsed. Strip redundant modifiers from your copied partial after upgrading, and re-test an entry containing &, ", and ' in the title. Verify in view-source, not devtools — devtools shows the decoded value and hides escaping bugs entirely.

Double-rendered meta tags. Many Statamic starter kits (and statamic/seo-pro migrations) already emit <title>/OG tags. If you switch to this addon, remove the other output first — duplicated <meta name="description"> is easy to miss and hurts audits. This matters more now that the addon escapes values, since the two outputs will differ byte-for-byte and neither will look obviously wrong.

Stache caching bites. SEO fields are content — after editing blueprints or fieldsets, run php artisan statamic:stache:clear. In production behind a full-page cache, remember the cached HTML holds the old tags; wire your SEO field save into whatever invalidation you already run for content saves. After a v1.5.1 upgrade, clear the full-page cache too — otherwise half your pages serve 1.5.0 canonicals and half serve 1.5.1, which is a miserable thing to debug.

Note the release-date anomaly. The metadata lists a release in 2026, which is almost certainly a stale or misparsed field. Treat "last release" as unverified and check the actual tag list on GitHub before assuming active maintenance. v1.5.0 → v1.5.1 is a small bugfix pair, not evidence of a release cadence.

noindex scope creep. A global "noindex" config toggle plus per-entry overrides is a classic conflict. Assert precedence explicitly: if global force-noindex is on (staging), no entry field should be able to override it. Set that in config, not in templates.

Debugging workflow. When tags don't appear: (1) php artisan about to confirm the addon booted, (2) check the exact tag name against src/Tags/, (3) ddd the tag output, (4) confirm the partial is inside <head> and not after </body> due to a misplaced stack. For canonical/escaping oddities specifically, curl -s https://site/blog?page=2 | grep canonical beats the browser every time.

Extension points to prefer over editing vendor code: published config, your own copy of the meta view, and blueprint/fieldset overrides in resources/. Anything you change under vendor/alt-design/ disappears on the next composer update — and given the pace of canonical/escaping changes, expect to re-copy that view again.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package