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

@dispatchcms/react

v0.0.6

Published

React hooks and provider for Dispatch CMS

Readme

@dispatchcms/react

React hooks and provider for Dispatch CMS. Use DispatchProvider and usePost / usePosts to fetch published posts in any React app (Next.js, Vite, etc.).

Install

npm install @dispatchcms/react
# or
pnpm add @dispatchcms/react
# or
yarn add @dispatchcms/react

Setup

Wrap your app with DispatchProvider and pass your site key (from Dispatch → Sites → your site). Read the key from your app’s config or environment (e.g. in Vite use import.meta.env.VITE_DISPATCH_SITE_KEY, or pass a literal in development):

import { DispatchProvider } from "@dispatchcms/react";

export default function App({ children }) {
  return (
    <DispatchProvider siteKey={import.meta.env.VITE_DISPATCH_SITE_KEY}>
      {children}
    </DispatchProvider>
  );
}

To use a different API base URL, pass the apiBase prop to the provider.

API

usePost(slug)

Fetches a single published post by slug.

  • Returns: { post: Post | null, isLoading: boolean, error?: string }
  • On failure or missing site key, does not throw; returns post: null, isLoading: false, and error set.

usePosts()

Fetches all published posts for the site.

  • Returns: { posts: Post[], isLoading: boolean, error?: string }
  • On failure or missing site key, does not throw; returns posts: [], isLoading: false, and error set.

Graceful failure

  • If the API request fails, hooks return post: null / posts: [], isLoading: false, and error: "Failed to load". The React tree is never crashed.
  • If no siteKey is provided to the provider, console.warn is logged and hooks return the same safe empty/error state with error: "Missing site key".

Types

  • Post – Post from the CMS. Fields include id, title, slug, content, excerpt, featured_image, published, published_at, created_at, updated_at, site_id. The content field is TipTap (ProseMirror) JSON.
  • UsePostResult / UsePostsResult – Return types of the hooks.

Rendering content

Post content is stored as TipTap (ProseMirror) JSON. Render it with TipTap’s static renderer so formatting, links, and images match the CMS:

  • React: Use renderToReactElement from @tiptap/static-renderer/pm/react with the same extensions as the CMS (e.g. StarterKit, or Document, Paragraph, Heading, Bold, Italic, Blockquote, BulletList, ListItem, Link, Image). No dangerouslySetInnerHTML needed.
  • HTML string: Use renderToHTMLString from @tiptap/static-renderer/pm/html-string (e.g. for SSR or non-React output).

For full parity with the CMS editor, use the same extensions (Document, Paragraph, Text, Heading, Bold, Italic, Blockquote, BulletList, ListItem, Link, Image). See TipTap static renderer docs.

import { renderToReactElement } from "@tiptap/static-renderer/pm/react";
import StarterKit from "@tiptap/starter-kit";

function TipTapContent({ content }: { content: unknown }) {
  if (content == null || typeof content !== "object") return null;
  return renderToReactElement({
    extensions: [StarterKit],
    content: content as { type: string; content?: unknown[] },
  });
}

// In your post page:
<TipTapContent content={post.content} />

Example

// List posts (e.g. homepage)
import { usePosts } from "@dispatchcms/react";

export default function HomePage() {
  const { posts, isLoading, error } = usePosts();

  if (isLoading) return <p>Loading…</p>;
  if (error) return <p>{error}</p>;

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <a href={`/blog/${post.slug}`}>{post.title}</a>
        </li>
      ))}
    </ul>
  );
}
// Single post page (e.g. /blog/:slug)
import { usePost } from "@dispatchcms/react";
import { TipTapContent } from "./TipTapContent"; // or inline renderToReactElement

export default function PostPage({ slug }) {
  const { post, isLoading, error } = usePost(slug);

  if (isLoading) return <p>Loading…</p>;
  if (error || !post) return <p>{error ?? "Not found"}</p>;

  return (
    <article>
      <h1>{post.title}</h1>
      {post.excerpt && <p>{post.excerpt}</p>}
      <TipTapContent content={post.content} />
    </article>
  );
}

License

MIT