Next.js Starter Kit
Words are nice... but code speaks louder. Dive into a fully commented project template, showcasing these techniques (and more) in action.
Live updates can be extremely useful both for content editors and regular visitors of your app/website:
Content-editors in Draft Mode can see their work-in-progress directly in the production website, without having to refresh the page;
Visitors can immediately see new content as it gets published, allowing all kinds of real-time interactions with your website/app (e.g., live-news coverage).
useQuerySubscription
hook The react-datocms
package exposes a useQuerySubscription
hook that uses our Real-time Updates API to make any Next.js page update in real-time.
We'll start with the following example, and modify it to activate real-time updates for any visitor of your website:
const PAGE_CONTENT_QUERY = `{allBlogPosts { id title }site: _site {favicon: faviconMetaTags { attributes content tag }}}`;export default async function Page() {const data = await performRequest(PAGE_CONTENT_QUERY);return <LatestBlogPosts data={data} />}
The first step is to build a <RealtimeLatestBlogPosts />
Client component, utilizing the useQuerySubscription
hook:
'use client';import { useQuerySubscription } from 'react-datocms';function RealtimeLatestBlogPosts({ subscription }) {const { data, error, status } = useQuerySubscription(subscription);return <LatestBlogPosts data={data} error={error} status={status} />;}
Then, in our page component, we can replace the <LatestBlogPosts />
component with <RealtimeLatestBlogPosts />
:
export default async function Page() {const data = await fetchContent();return (<RealtimeLatestBlogPostssubscription={{query: PAGE_CONTENT_QUERY,initialData: data,token: process.env.NEXT_DATOCMS_API_TOKEN,}}/>);}
useQuerySubscription
Perhaps a more common scenario is activating real-time updates not for every visitor, but only for content editors in Draft Mode, and also showing records in draft:
In this case, the page component will change a bit, as we need to check draft mode activation and either render <RealtimeLatestBlogPosts />
or <LatestBlogPosts />
:
function fetchContent({ includeDrafts }) {return ;}export default async function Page() {const { isEnabled } = draftMode();const data = await performRequest(PAGE_CONTENT_QUERY, { includeDrafts: isEnabled });if (isEnabled) {return (<RealtimeLatestBlogPostssubscription={{query: PAGE_CONTENT_QUERY,initialData: data,environment: process.env.NEXT_DATOCMS_ENVIRONMENT,token: process.env.NEXT_DATOCMS_API_TOKEN,}}/>);}return <LatestBlogPosts data={data} />}
In summary, the pattern to follow on every page is this:
Do not place the actual content of the page directly inside the Page
component, but in a secondary component (ie. <Content />
);
Create a real-time wrapper component (ie. <Realtime />
) that utilizes the useQuerySubscription
hook, and then renders the <Content />
;
Create the actual Page component and have it return either <Realtime />
, or <Content />
based on whether draft mode is active or not.
Repeating this pattern for each page can become repetitive and prone to errors, but it is possible to make the code extremely compact and DRY (Don't Repeat Yourself) by using helper functions that generate both the <Page />
and <Realtime />
components for you. This way, you can focus solely on the <Content />
component, which is what actually contains the content of your page.
To see an example of these helper functions, we recommend you take a look at the code of one of our Next.js Starter Kit — for instance, this is a page component, this is a real-time component, while this is the actual content — but of course, you can customize them as you prefer to best fit them into your project.
If, however, you want to directly see the end result and the experience for editors, we recommend launching the starter from here: