π SSR-SEO
Make your React SPA SEO-friendly without SSR complexity. SSR-SEO automatically generates static HTML snapshots for search engine crawlers while serving your dynamic React app to users.
π₯ Project Status: Early / New
β οΈ This is a brand new project and actively evolving.
SSR-SEO is in early-stage development. APIs, defaults, and integration patterns may still change as real-world usage expands.
That said:
- The core snapshot + bot-detection logic is stable
- The Lovable integration pattern is intentional
- The guardrails below are important
If you hit something odd, that is expected at this stage. Please report it.
π Issues & feedback: https://github.com/Azerax/ssr-seo-tracker/issues
β¦
π SSR-SEO
Make your React SPA SEO-friendly without SSR complexity. SSR-SEO automatically generates static HTML snapshots for search engine crawlers while serving your dynamic React app to users.
π₯ Project Status: Early / New
β οΈ This is a brand new project and actively evolving.
SSR-SEO is in early-stage development. APIs, defaults, and integration patterns may still change as real-world usage expands.
That said:
- The core snapshot + bot-detection logic is stable
- The Lovable integration pattern is intentional
- The guardrails below are important
If you hit something odd, that is expected at this stage. Please report it.
π Issues & feedback: https://github.com/Azerax/ssr-seo-tracker/issues
β¨ Features
- π€ Smart Bot Detection - Automatically identifies search engine crawlers
- πΈ HTML Snapshots - Generates static HTML for perfect SEO indexing
- πΊοΈ Dynamic Sitemap - Auto-generates sitemaps from your routes
- π€ Robots.txt Management - Configure crawler access rules
- π Monitoring Dashboard - Track SEO health and issues
- π§ Auto-Fix - Automatically resolves common SEO issues
- π¨ Beautiful UI - Pre-built dashboard components
- π¦ Zero Config - Works out of the box with sensible defaults
β οΈ Important: What This Package Does NOT Do
- Does NOT create routes - You own your routing; we donβt inject anything
- Does NOT take over
/- Your appβs root route remains yours - Dashboard is opt-in - Add
/ssr-seo/*routes only if you want the dashboard UI
π¦ Installation
npm install ssr-seo
# or
yarn add ssr-seo
# or
pnpm add ssr-seo
Peer Dependencies
SSR-SEO requires React as a peer dependency:
npm install react react-dom
π Lovable Demo (Hackathon Build)
A live Lovable demo of SSR-SEO is available here:
https://important-cats.lovable.app/ssr-seo/status
Notes:
- Created during the Lovable hackathon the weekend of Jan 2
- Currently under freeze while judges review submissions
- May lag behind the latest package changes
π Integration Patterns
SSR-SEO supports two integration patterns depending on your app architecture.
Pattern 1: Stand-alone (Lovable-style)
Best for apps built with Lovable or where you want SSR-SEO to manage the Supabase connection.
Step 1: Set environment variables
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key
Step 2: Wrap your app
import { SSRSEOProvider } from 'ssr-seo';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<SSRSEOProvider>
<Routes>
{/* YOUR routes - SSR-SEO does not create or own any routes */}
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</SSRSEOProvider>
</BrowserRouter>
);
}
SSR-SEO will automatically create a Supabase client from the environment variables.
Pattern 2: Controlled (Advanced Apps)
Best for apps that already have a Supabase client or need full control over the connection.
Step 1: Create your Supabase client
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
'https://your-project.supabase.co',
'your-anon-key'
);
Step 2: Pass the client to SSRSEOProvider
import { SSRSEOProvider } from 'ssr-seo';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { supabase } from './lib/supabase';
function App() {
return (
<BrowserRouter>
<SSRSEOProvider supabaseClient={supabase}>
<Routes>
{/* YOUR routes - SSR-SEO does not create or own any routes */}
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</SSRSEOProvider>
</BrowserRouter>
);
}
Optional: Add Dashboard Routes
The dashboard is completely opt-in. If you want the monitoring UI, add the routes yourself:
import { SSRSEOProvider, SSRSEODashboardRoutes } from 'ssr-seo';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<SSRSEOProvider>
<Routes>
{/* Your routes */}
<Route path="/" element={<Home />} />
{/* Opt-in: SSR-SEO Dashboard under /ssr-seo/* */}
{SSRSEODashboardRoutes}
</Routes>
</SSRSEOProvider>
</BrowserRouter>
);
}
Dashboard routes provided:
| Route | Description |
|---|---|
/ssr-seo/status | Setup verification and health checks |
/ssr-seo/config | SEO configuration and settings |
/ssr-seo/monitoring | Real-time monitoring and alerts |
π€ Important: Lovable Integration Guardrails (READ THIS FIRST)
If you are using Lovable, this section is critical.
The actual problem
Lovable will not run init the way you expect. Instead, it tries to "do it itself" by scaffolding and restructuring, and that breaks SSR-SEO.
The good news: when this happens, the damage is usually contained to the /ssr-seo directory, so it is fixable. The bad news: undoing it costs Lovable prompts/credits.
β The correct approach
You must explicitly prompt Lovable to read and implement LOVABLE.md exactly as written.
Also tell Lovable:
- Do not scaffold or restructure the project
- Do not try to re-initialize anything
- Follow the steps in LOVABLE.md exactly
β Safe Lovable prompt (copy/paste)
Use this prompt verbatim:
Read LOVABLE.md and implement it exactly as written.
Do not scaffold or restructure the project.
Do not "init" the project in your own way.
Follow the LOVABLE.md steps exactly and only change what it instructs.
β οΈ What happens if you skip this
If Lovable scaffolds on its own:
- It can break SSR-SEO (usually inside /ssr-seo)
- It is fixable, but it costs prompts and credits to undo
- That time and cost is avoidable if LOVABLE.md is followed first
π LOVABLE.md is Required Reading
If you are using Lovable:
- LOVABLE.md is not optional
- It exists specifically to prevent tool-driven breakage
- Skipping it is the fastest way to lose time and credits
π API Reference
SSRSEOProvider
The main provider component that wraps your application.
interface SSRSEOProviderProps {
children: React.ReactNode;
// Supabase connection (choose one):
supabaseClient?: SupabaseClient; // Pass your own client (Pattern 2)
supabaseUrl?: string; // Or provide URL + key
supabaseAnonKey?: string; // (auto-reads from VITE_* env vars)
// Behavior options:
autoSetup?: boolean; // Auto-initialize on mount (default: true)
autoScan?: boolean; // Auto-scan for routes (default: true)
scanIntervalHours?: number; // Hours between scans (default: 24)
dashboardBasePath?: string; // Dashboard URL base (default: '/ssr-seo')
}
useSSRSEO Hook
Access SEO context from any component.
import { useSSRSEO } from 'ssr-seo';
function MyComponent() {
const {
supabase, // The Supabase client (or null if not configured)
isSupabaseConnected, // Boolean: is Supabase available?
isBot, // Is current request from a bot?
config, // Current SEO config
refreshSnapshots, // Trigger snapshot refresh
runScan // Run an SEO scan
} = useSSRSEO();
if (!isSupabaseConnected) {
return <div>Please configure Supabase to use SEO features</div>;
}
return (
<div>
{isBot ? 'Bot detected!' : 'Human user'}
<button onClick={runScan}>Run SEO Scan</button>
</div>
);
}
SEOHead Component
Set page-specific SEO metadata.
import { SEOHead } from 'ssr-seo';
function BlogPost({ post }) {
return (
<>
<SEOHead
title={post.title}
description={post.excerpt}
keywords={post.tags}
ogImage={post.image}
ogType="article"
canonicalUrl={`https://mysite.com/blog/${post.slug}`}
/>
<article>{/* content */}</article>
</>
);
}
Utility Functions
import {
detectBot,
generateSitemap,
generateRobotsTxt,
validateMetaDescription,
validateTitle,
} from 'ssr-seo';
const isBot = detectBot(userAgent);
const sitemap = await generateSitemap('https://mysite.com');
const titleResult = validateTitle('My Page Title');
// { valid: true, length: 13, issues: [] }
βοΈ Database Setup
SSR-SEO uses Supabase for data storage. Required tables:
seo_snapshots- Stores HTML snapshotsseo_config- Stores configurationseo_monitoring- Stores scan resultsseo_alerts- Stores SEO issues
If using the dashboard, navigate to /ssr-seo/status to verify your setup. The dashboard will show you what is missing.
π― Common Use Cases
E-commerce Product Pages
import { SEOHead } from 'ssr-seo';
function ProductPage({ product }) {
return (
<>
<SEOHead
title={`${product.name} | My Store`}
description={product.description.slice(0, 160)}
ogImage={product.images[0]}
ogType="product"
/>
<ProductDetails product={product} />
</>
);
}
Blog with Dynamic Content
import { useSSRSEO } from 'ssr-seo';
function BlogLayout({ children }) {
const { refreshSnapshots, isSupabaseConnected } = useSSRSEO();
useEffect(() => {
if (newPostPublished && isSupabaseConnected) {
refreshSnapshots();
}
}, [newPostPublished, isSupabaseConnected]);
return <div>{children}</div>;
}
Manual Snapshot Generation
import { generateSnapshot } from 'ssr-seo';
async function createSnapshot(path: string) {
const snapshot = await generateSnapshot(path);
console.log('Generated snapshot:', snapshot.metadata);
}
π§ Troubleshooting
"Supabase Not Configured" Error
This appears when SSR-SEO cannot connect to Supabase. Fix it by:
- Pattern 1: Set
VITE_SUPABASE_URLandVITE_SUPABASE_ANON_KEYenv vars - Pattern 2: Pass
supabaseClientprop toSSRSEOProvider
Snapshots Not Generating
- Verify Supabase connection at
/ssr-seo/status - Check that routes are being detected
- Ensure your components render content synchronously
- Check browser console for errors
Bot Detection Not Working
import { detectBot } from 'ssr-seo';
console.log(detectBot('Googlebot/2.1')); // true
console.log(detectBot('Mozilla/5.0')); // false
Dashboard Not Loading
- Ensure
SSRSEODashboardRoutesis added to your Routes - Check you are navigating to
/ssr-seo/status - Verify Supabase is configured (see above)
π Issues & Feedback
This project uses a dedicated public issue tracker.
π Report bugs, request features, or share feedback here: https://github.com/Azerax/ssr-seo-tracker/issues
Note: The source code is maintained in a private repository. This repo exists only for issues and feedback.
π License
MIT Β© Scott Holmes
π Links
- NPM Package: https://www.npmjs.com/package/ssr-seo
- Issue Tracker: https://github.com/Azerax/ssr-seo-tracker/issues
- Changelog: ./CHANGELOG.md
Made with love for the React community