EarlySEO LogoEarlySEO Docs

React SDK Guide

Use @earlyseo/blog in React apps (Vite, Remix, SPA) with provider, components, and hooks.

Open .mdx

Overview

This guide covers using EarlySEO in client-side React apps — Vite, Remix, or any SPA setup. For Next.js App Router, use the Next.js SDK Guide instead, which supports server components and the npx @earlyseo/blog init CLI scaffold.


Prerequisites

  • A React project (Vite, Remix, Create React App, etc.)
  • SDK integration enabled in EarlySEO (Dashboard → Integrations → SDK)
  • Site ID from the SDK integration card
  • At least one published article

1. Install package

npm install @earlyseo/blog

2. Add provider

Wrap your app (or the blog section) with EarlySeoProvider. This makes the site ID and base path available to all child components.

import { EarlySeoProvider } from "@earlyseo/blog/react";

export function AppProviders({ children }: { children: React.ReactNode }) {
  return (
    <EarlySeoProvider siteId="your-site-id" basePath="/blog">
      {children}
    </EarlySeoProvider>
  );
}
PropTypeDescription
siteIdstringYour EarlySEO site ID (required)
basePathstringURL prefix for blog routes (default: /blog)

3. Render built-in components

Use the pre-built components for a zero-config blog UI:

import { BlogList, BlogPost, ArticleStyles } from "@earlyseo/blog/react";

// Blog listing page
export function BlogPage() {
  return (
    <>
      <ArticleStyles />
      <BlogList />
    </>
  );
}

// Single article page
export function BlogPostPage({ slug }: { slug: string }) {
  return (
    <>
      <ArticleStyles />
      <BlogPost slug={slug} />
    </>
  );
}
ComponentDescription
ArticleStylesInjects EarlySEO CSS styles into the page
BlogListRenders a paginated article list with links
BlogPostRenders a single article by slug

4. Headless mode with hooks

Build a fully custom UI using the data hooks:

import { useArticles, useArticle } from "@earlyseo/blog/react";

export function CustomList() {
  const { articles, loading } = useArticles();
  if (loading) return <div>Loading...</div>;

  return (
    <div>
      {articles.map((article) => (
        <a key={article.slug} href={`/blog/${article.slug}`}>
          <h3>{article.title}</h3>
          <p>{article.metaDescription}</p>
        </a>
      ))}
    </div>
  );
}

export function CustomPost({ slug }: { slug: string }) {
  const { article, loading } = useArticle(slug);
  if (loading) return <div>Loading...</div>;
  if (!article) return <div>Not found</div>;

  return <div dangerouslySetInnerHTML={{ __html: article.contentRawHtml }} />;
}
HookReturnsDescription
useArticles(){ articles, loading }Fetches paginated article list
useArticle(slug){ article, loading }Fetches a single article by slug

How publishing works

  1. Publish an article from the EarlySEO dashboard
  2. CDN JSON files update automatically
  3. Your React app reads the updated JSON on the next fetch
  4. No rebuild or redeploy needed

Routing example (React Router)

import { Routes, Route } from "react-router-dom";

function App() {
  return (
    <EarlySeoProvider siteId="your-site-id" basePath="/blog">
      <Routes>
        <Route path="/blog" element={<BlogPage />} />
        <Route path="/blog/:slug" element={<BlogPostRoute />} />
      </Routes>
    </EarlySeoProvider>
  );
}

function BlogPostRoute() {
  const { slug } = useParams();
  return <BlogPostPage slug={slug!} />;
}

Troubleshooting

No content returned

  • Verify your siteId is correct
  • Confirm the integration type is SDK in the dashboard
  • Confirm the article status is published

CORS issues

  • Requests go directly to media.earlyseo.com — no proxy needed
  • If using a custom proxy, ensure CORS headers are set

Inconsistent styling

  • Always render <ArticleStyles /> before <BlogList /> or <BlogPost />
  • For fully custom styles, use contentRawHtml from the hooks and apply your own CSS

Need help?

On this page