Skip to main content

Render on the client

Fetch and render a template in the browser, from a client component in your template route. Use this path for a single-page app, or for a route where search engines and social previews don't matter. The example on this page is the Blog post template, rendering the post AI 101 at /blog/ai-101 in the Blog project.

Prerequisites​

What this is and what you get​

On this path, your template route fetches the composition and renders it in the browser. You get a template that renders at its URL, your own loading and error states, and a refetchData function to call when an entry changes.

The HTML your server sends has no template content, title or meta tags in it. When search engines or social previews need those, use Render on the server instead. CSR or SSR explains the difference.

Fetch the composition with useCompositionData​

useCompositionData(query, options?) fetches the composition and the data it's bound to. The query takes url, compositionUid or templateContentTypeUid. The options take locale, variantAlias and templateEntryUid.

In a catch-all route, pass the route's own path as url, such as /blog/ai-101:

const pathname = usePathname();
const { specOptions, isLoading, error } = useCompositionData({ url: pathname });

The hook reads the query string from the browser's address itself. Unlike on the server, there's no searchQuery to pass.

Fetching by url needs your addresses to hold values stored in your entries. When they don't, such as with a Custom Preview URL that uses transformation rules, fetch by template and entry instead. See Fetch by template and entry instead of by URL.

Render the loading, error and loaded states​

The hook's result is always in one of three states:

StateisLoadingerrorspecOptions
Loadingtruenullnull
FailedfalseThe errornull
LoadedfalsenullThe spec options

Check isLoading first, then error, then render <StudioComponent specOptions={specOptions} />.

A URL that no template matches doesn't give you an empty result. The hook puts the error in error, with a code such as NO_COMPOSITION_FOR_URL. Pass it to isCompositionNotFound. When that's true, show your not-found content. Otherwise, show the error. For which codes count as not found, see Send a 404 when no template matches. error is typed unknown, so check error instanceof Error before you read error.message.

"use client";

import {
StudioComponent,
isCompositionNotFound,
useCompositionData,
} from "@contentstack/studio-react";
import { usePathname } from "next/navigation";

export function TemplateView() {
const pathname = usePathname();
const { specOptions, isLoading, error } = useCompositionData({ url: pathname });

if (isLoading) return <p>Loading</p>;
if (error) {
if (isCompositionNotFound(error)) return <p>Post not found</p>;
return <p>{error instanceof Error ? error.message : "Something went wrong"}</p>;
}
return <StudioComponent specOptions={specOptions} />;
}

<StudioComponent /> also takes a data prop. Pass it only for values from outside Contentstack. See StudioComponent data.

Refresh the template when an entry changes​

The hook also returns refetchData, which fetches the template's bound data again. That's all a live edit changes. The current template stays on screen while it fetches. It doesn't go back to the loading state.

The SDK doesn't subscribe to Live Preview itself. Call refetchData from Live Preview's onEntryChange, wired once in your app shell as Install Live Preview describes.

Why there's no style or head step on the client​

  • Styles. getSSRStyleTags is for HTML built on the server, before any browser script runs. On the client there's no such HTML, so you don't call it.
  • Title and meta tags. specOptions.seo has the template's SEO values, but only after the fetch, in the browser. Crawlers and social previews that don't run JavaScript never see them. To put them in the first HTML, see Render on the server.

Check the template renders on the client​

Deploy the Blog post template first, because outside Studio your site reads only deployed templates. See Check that a template opens at its URL.

Open http://localhost:3106/blog/ai-101 in a browser. Your loading state shows first, because the hook starts in its loading state. Then the Blog post template renders with AI 101's content.

The Blog post template rendered at http://localhost:3106/blog/ai-101 in a browser, on the client-rendered example site, showing AI 101&#39;s title and summary.

If you see your error state instead, see If it doesn't work.

If it doesn't work​

  • The loading state lasts about 30 seconds, then error is set, with the code INIT_NOT_COMPLETE. The message starts [Contentstack Studio] INIT_NOT_COMPLETE: A composition was requested before Studio finished starting up. The route rendered before studioSdk.init ran, or the init never ran. The message calls the init initStudio(). It means studioSdk.init. See Make sure the init runs in the browser.

The example site&#39;s error state on /blog/ai-101 after about 30 seconds, showing the INIT_NOT_COMPLETE error, A composition was requested before Studio finished starting up, with its list of what to do.

  • specOptions stays null. Read error. A not-found code means no template's URL matches the path, or no entry has that URL. Check the template's URL in Studio. See How template URLs work.
  • The title and meta tags show in the browser but not in social previews. The route renders on the client, and social networks don't run JavaScript. See SEO and metadata, then Render on the server.

Report a problem with this page