@dsaplatform/content-sdk
v1.8.0
Published
React SDK for DSA Content OS and Funnel OS — fetch and render SEO content, embed lead capture forms, run multi-step funnels and quizzes
Downloads
1,112
Maintainers
Readme
@dsaplatform/content-sdk
React SDK for DSA Content Operating System — fetch and render SEO-optimized content from a central content engine on any Next.js or React site.
Installation
npm install @dsaplatform/content-sdkQuick Start — Next.js App Router (SSR)
1. Server-side data fetching in app/blog/[slug]/page.tsx
import { fetchArticleBySlug, fetchRelatedArticles, generateArticleMetadata } from '@dsaplatform/content-sdk/server';
const config = {
apiUrl: process.env.CONTENT_ENGINE_URL!,
apiKey: process.env.CONTENT_API_KEY!,
cacheStrategy: 'revalidate' as const,
revalidateSeconds: 3600,
};
export async function generateMetadata({ params }: { params: { slug: string } }) {
const article = await fetchArticleBySlug(config, params.slug);
return generateArticleMetadata(article, 'https://yoursite.com');
}
export default async function BlogPost({ params }: { params: { slug: string } }) {
const article = await fetchArticleBySlug(config, params.slug);
const related = await fetchRelatedArticles(config, params.slug, 3);
return (
<ArticlePageWrapper article={article} related={related} />
);
}2. Client component for rendering
'use client';
import { ArticlePage, SeoMetaBridge } from '@dsaplatform/content-sdk';
export function ArticlePageWrapper({ article, related }) {
return (
<>
<SeoMetaBridge article={article} siteUrl="https://yoursite.com" />
<ArticlePage
article={article}
showFaq
showTableOfContents
showRelated
relatedArticles={related}
onRelatedClick={(slug) => window.location.href = `/blog/${slug}`}
/>
</>
);
}3. Article listing in app/blog/page.tsx
import { fetchArticleList } from '@dsaplatform/content-sdk/server';
import { ArticleFeed } from '@dsaplatform/content-sdk';
export default async function BlogIndex() {
const { items } = await fetchArticleList(config, { page: 1, per_page: 12 });
return (
<ArticleFeed
articles={items}
layout="grid"
columns={3}
onArticleClick={(slug) => `/blog/${slug}`}
/>
);
}Client-Side Hooks (SPA / CSR)
Wrap your app with the provider:
import { DsaContentProvider } from '@dsaplatform/content-sdk';
<DsaContentProvider config={{ apiUrl: '...', apiKey: '...' }}>
<App />
</DsaContentProvider>Then use hooks anywhere:
import { useArticles, useArticle, useRelatedArticles, useCategories } from '@dsaplatform/content-sdk';
function BlogList() {
const { articles, loading, pagination } = useArticles({ page: 1, per_page: 10 });
if (loading) return <p>Loading...</p>;
return <ArticleFeed articles={articles} />;
}
function BlogPost({ slug }: { slug: string }) {
const { article, loading } = useArticle(slug);
const { articles: related } = useRelatedArticles(slug, 3);
if (loading || !article) return <p>Loading...</p>;
return <ArticlePage article={article} showRelated relatedArticles={related} />;
}Theming & Styling
Light / Dark theme (works out of the box)
The SDK ships built-in prose styles for headings, paragraphs, lists, blockquotes, tables, and links inside the article body. These are scoped via [data-dsa-article-body] and injected once via <style> — they work even when Tailwind Preflight resets all margins.
<ArticlePage article={article} theme="light" />
<ArticlePage article={article} theme="dark" />Override any value with CSS variables on a parent element:
:root {
--dsa-text: #0f172a;
--dsa-content-text: #334155;
--dsa-h2-text: #0f172a;
--dsa-h3-text: #1e293b;
--dsa-link: #2563eb;
--dsa-link-hover: #1d4ed8;
--dsa-toc-bg: #f8fafc;
--dsa-card-border: #e2e8f0;
--dsa-badge-bg: #eff6ff;
--dsa-badge-text: #2563eb;
--dsa-badge-alt-bg: #f0fdf4;
--dsa-badge-alt-text: #16a34a;
--dsa-divider: #e2e8f0;
--dsa-blockquote-border: #cbd5e1;
--dsa-blockquote-text: #475569;
--dsa-pre-bg: #f1f5f9;
--dsa-table-border: #e2e8f0;
--dsa-table-header-bg: #f8fafc;
}To disable the built-in prose styles (e.g. if you use @tailwindcss/typography):
<ArticlePage article={article} disableProseStyles />Inherit theme (full control)
theme="inherit" removes all inline styles and injected <style> tags. The SDK renders only semantic HTML with stable CSS classes and data-* attributes.
<ArticlePage
article={article}
theme="inherit"
className="max-w-3xl mx-auto font-sans"
contentClassName="prose dark:prose-invert"
/>Tailwind + Preflight note: In
inheritmode, paragraph margins, heading sizes, and list styles are all reset to zero by Preflight. You must provide your own prose styles — for example via@tailwindcss/typography(proseclass) or custom CSS targeting the stable selectors below.
Stable CSS selectors
Always present regardless of theme:
| Element | CSS class | Data attribute |
|---------|-----------|----------------|
| Root | your className | data-dsa-theme="light\|dark\|inherit" |
| Article body | dsa-article-body + contentClassName | data-dsa-article-body |
| TOC nav | dsa-toc | data-dsa-toc |
| Meta row | dsa-meta | data-dsa-meta |
| H1 | dsa-h1 | — |
| Badge | dsa-badge | — |
| Featured image | dsa-featured-image | — |
| Divider | dsa-divider | — |
Components
| Component | Description |
|-----------|-------------|
| ArticleFeed | Grid/list of article cards |
| ArticlePage | Full article with TOC, FAQ, related |
| FaqBlock | FAQ section with Schema.org markup |
| RelatedArticles | Related articles widget |
| SeoMetaBridge | JSON-LD structured data |
Server Helpers
| Function | Description |
|----------|-------------|
| fetchArticleBySlug(config, slug) | Get one article |
| fetchArticleList(config, filters?) | Paginated article list |
| fetchRelatedArticles(config, slug, limit?) | Related articles |
| fetchCategories(config) | Pillar/cluster tree |
| fetchSitemap(config) | All published slugs |
| generateArticleMetadata(article, siteUrl?) | Next.js Metadata object |
Configuration
interface DsaContentConfig {
apiUrl: string; // Content Engine URL
apiKey: string; // Site's public API key
cacheStrategy?: 'no-cache' | 'force-cache' | 'revalidate';
revalidateSeconds?: number; // ISR interval
}Sitemap Generation
Next.js App Router handles sitemaps natively. The SDK exposes fetchSitemap — use it in app/sitemap.ts:
// app/sitemap.ts
import { fetchSitemap } from '@dsaplatform/content-sdk/server';
const config = {
apiUrl: process.env.CONTENT_ENGINE_URL!,
apiKey: process.env.CONTENT_API_KEY!,
};
export default async function sitemap() {
const slugs = await fetchSitemap(config);
const base = process.env.NEXT_PUBLIC_SITE_URL || 'https://yoursite.com';
return slugs.map((slug) => ({
url: `${base}/blog/${slug}`,
lastModified: new Date(),
changeFrequency: 'weekly' as const,
priority: 0.8,
}));
}Next.js will serve /sitemap.xml automatically — no additional packages needed.
IndexNow (Instant Search Engine Indexing)
IndexNow lets Bing and Yandex index new pages within minutes of publishing.
The Content OS handles IndexNow submission automatically when you configure the key in your site settings. You only need to verify domain ownership:
Setup (one-time, per domain)
Step 1. In your Content OS admin → Site Settings → enter your IndexNow key.
Step 2. Host a verification file at the root of your site. Create a static text file containing only the key (no whitespace):
// public/bbf9f3f15c3e4342a912cd0c105516b0.txt
bbf9f3f15c3e4342a912cd0c105516b0In Next.js, files in /public are served at the root automatically. Bing will verify at:
https://www.yoursite.com/bbf9f3f15c3e4342a912cd0c105516b0.txtStep 3. Done — every new article published by Content OS is automatically submitted to api.indexnow.org.
Note: The key file must be UTF-8 encoded and contain the key only. If you use a CDN or middleware that rewrites routes, make sure
/{key}.txtis excluded from rewrites and returns a plain text200response.
License
MIT
