Universal script

A single <script> tag that injects SEO meta tags into any HTML page. Works with React, Vue, Svelte, Astro, Rails, Django, plain HTML — anything that renders a document.

Install

<!-- in the <head> of your document -->
<script
  src="https://cdn.seoinjector.io/v1.js"
  data-project="proj_xxxxxxxxxxxx"
  async></script>

The script runs once on initial load, then again on every client-side route change in single-page apps.

Configuration (data-* attributes)

Attribute Default Purpose
data-project Required. Your project ID from the dashboard.
data-env production Switch between staging / production projects.
data-spa true Listen to history.pushState + popstate and re-inject.
data-route location.pathname Override the route key used for lookup.
data-fallback keep keep existing tags if no meta is found, or strip.
data-debug false Log injected tags + cache state to the console.
data-cache 60 Client cache TTL in seconds (0 to disable).

Programmatic API

The script exposes a global window.SEOInjector:

// re-fetch and inject for the current route
SEOInjector.refresh();

// load a specific route (useful for prerendered shells)
SEOInjector.load('/blog/hello-world');

// set meta manually (skips the network)
SEOInjector.set({
  title: 'Hello, world',
  description: 'A short post about hello.',
  og: { image: 'https://example.com/og.png' },
});

// subscribe to inject events
SEOInjector.on('injected', (meta) => console.log('new meta', meta));

Single-page app examples

React (React Router)

import { useLocation } from 'react-router-dom';
import { useEffect } from 'react';

export function SEOSync() {
  const loc = useLocation();
  useEffect(() => { window.SEOInjector?.load(loc.pathname); }, [loc]);
  return null;
}

Vue Router

router.afterEach((to) => {
  window.SEOInjector?.load(to.fullPath);
});

SvelteKit

import { afterNavigate } from '$app/navigation';
afterNavigate(({ to }) => window.SEOInjector?.load(to?.url.pathname));

Plain HTML / MPA

Nothing extra. The script runs on every full page load.

Performance

  • ~3 KB gzipped, loaded async, no render-blocking.
  • Edge-cached responses average ~12 ms globally.
  • Zero impact on Core Web Vitals — meta tags don't affect LCP/CLS.

Troubleshooting

  • Tags not updating in SPA? Make sure your router triggers history.pushState, or call SEOInjector.load() manually.
  • Duplicate tags? Set data-fallback="strip" to let SEO Injector own the head fully.
  • CSP errors? Add https://cdn.seoinjector.io to script-src and https://api.seoinjector.io to connect-src.