BrandApp Playground

CMS Hooks

Read-only · SDK 1.8

TanStack Query hooks for fetching CMS posts, groups, and tags. Authoring (create / update / delete) lives in Reopt Studio.

usePostList

Fetch and cache posts with optional status, group, search, and pagination filters. Returns CmsPaginatedResponse<Post>.

components/post-list.tsx
import { usePostList } from "@reopt-ai/brandapp-sdk/react/hooks";
import type { ReoptSDK } from "@reopt-ai/brandapp-sdk";

function PostList({ sdk }: { sdk: ReoptSDK }) {
  const { data, isLoading, error } = usePostList(sdk, {
    status: "published",
    limit: 20,
  });

  if (isLoading) return <p>Loading posts...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <ul>
      {data?.records.map((post) => (
        <li key={post.id}>
          <a href={`/blog/${post.slug}`}>{post.title}</a>
          <span>{post.status}</span>
        </li>
      ))}
    </ul>
  );
}

usePost

Fetch a single post by id (returns PostDetail with document.contentRich).

components/post-detail.tsx
import { usePost } from "@reopt-ai/brandapp-sdk/react/hooks";
import type { ReoptSDK } from "@reopt-ai/brandapp-sdk";

function PostDetail({ sdk, postId }: { sdk: ReoptSDK; postId: string }) {
  const { data: post } = usePost(sdk, postId);
  if (!post) return null;
  return <article>{post.title}</article>;
}

usePostBySlug

Resolve a post by its slug without an id mapping — ideal for a /blog/[slug] route. Server components can call sdk.cms.posts.getBySlug directly to avoid an extra HTTP hop.

components/blog-article.tsx
import { usePostBySlug } from "@reopt-ai/brandapp-sdk/react/hooks";
import type { ReoptSDK } from "@reopt-ai/brandapp-sdk";

// Resolve a post by its slug (e.g. for a /blog/[slug] route on your own site).
// The 3rd arg scopes the lookup to a post group; the 4th passes React Query options.
function BlogArticle({ sdk, slug }: { sdk: ReoptSDK; slug: string }) {
  const { data: post, isLoading } = usePostBySlug(sdk, slug);

  if (isLoading) return <p>Loading...</p>;
  if (!post) return <p>Not found</p>;
  return <article>{post.title}</article>;
}

usePostGroupList

List post groups (categories). Returns CmsPaginatedResponse<PostGroup> with postCount per group.

components/group-nav.tsx
import { usePostGroupList } from "@reopt-ai/brandapp-sdk/react/hooks";
import type { ReoptSDK } from "@reopt-ai/brandapp-sdk";

// Returns CmsPaginatedResponse<PostGroup> (records, total, page, ...).
function GroupNav({ sdk }: { sdk: ReoptSDK }) {
  const { data } = usePostGroupList(sdk, { sortBy: "name" });

  return (
    <nav>
      {data?.records.map((group) => (
        <a key={group.id} href={`/blog/${group.slug ?? group.id}`}>
          {group.name} ({group.postCount})
        </a>
      ))}
    </nav>
  );
}

usePostGroup

Fetch a single post group by id (returns PostGroup).

components/group-header.tsx
import { usePostGroup } from "@reopt-ai/brandapp-sdk/react/hooks";
import type { ReoptSDK } from "@reopt-ai/brandapp-sdk";

function GroupHeader({ sdk, groupId }: { sdk: ReoptSDK; groupId: string }) {
  const { data: group } = usePostGroup(sdk, groupId);
  if (!group) return null;
  return (
    <header>
      <h1>{group.name}</h1>
      <p>{group.postCount} posts</p>
    </header>
  );
}

useCmsTags

List the tags used across the brand scope. Returns Tag[] where each entry is { id, text, postCount }.

components/tag-cloud.tsx
import { useCmsTags } from "@reopt-ai/brandapp-sdk/react/hooks";
import type { ReoptSDK } from "@reopt-ai/brandapp-sdk";

// Tag[] — each entry is { id, text, postCount }. Optionally scope by groupId.
function TagCloud({ sdk, groupId }: { sdk: ReoptSDK; groupId?: string }) {
  const { data: tags } = useCmsTags(sdk, groupId ? { groupId } : undefined);

  return (
    <div>
      {tags?.map((tag) => (
        <span key={tag.id}>
          {tag.text} ({tag.postCount})
        </span>
      ))}
    </div>
  );
}