npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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

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-sdk

Quick 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 inherit mode, 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 (prose class) 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
bbf9f3f15c3e4342a912cd0c105516b0

In Next.js, files in /public are served at the root automatically. Bing will verify at:

https://www.yoursite.com/bbf9f3f15c3e4342a912cd0c105516b0.txt

Step 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}.txt is excluded from rewrites and returns a plain text 200 response.

License

MIT