When you have private models in Builder.io, you can securely enable preview functionality using the builder.authToken
property in the Gen1 SDK. This approach keeps your private API key secure on the server while allowing content editors to preview draft content in the Visual Editor.
The builder.authToken
property authenticates your server-side requests to access private model content without exposing your private API key to client-side code. This is essential for maintaining security while providing a seamless editing experience.
Note: Setting builder.authToken
is only needed if using the Gen 1 SDK. Gen 2 SDKs are stateless, and therefore do not require this additional step.
Before you begin, ensure you have:
- A private model configured in Builder.
- A private API key generated in your Organization Settings.
- A Next.js project with the React Gen 1 SDK (
@builder.io/react
) installed. - Server-side rendering setup using
getStaticProps
or similar.
If you haven't already created a private model, follow the instructions in Creating a Private Model to set up your model with the Public Readable toggle switched off.
Configure your private API key on your server using builder.authToken
. This property should only be set in server-side contexts like getStaticProps
, API routes, or server components.
Add your private API key to your environment variables:
# .env.local
BUILDER_PRIVATE_API_KEY=your_private_api_key_here
In your page component, set authToken
during server-side data fetching:
// pages/[[...index]].tsx
import type { GetStaticProps } from "next";
builder.init("your-public-api-key");
const modelName = "private-landing-pages";
export const getStaticProps: GetStaticProps = async (opts) => {
let pathName = "/" + ((opts.params?.index as string[])?.join("/") || "");
// Set private key for server-side access only
builder.authToken = process.env.BUILDER_PRIVATE_API_KEY;
const page = await builder
.get(modelName, {
userAttributes: { urlPath: pathName }
})
.promise();
return {
props: { page: page || null },
revalidate: 5,
};
};
Important: Never set builder.authToken
in client-side code or expose your private API key in your front-end compiled code.
Use the useIsPreviewing()
hook to detect when content is being previewed in the Visual Editor. This ensures your page renders correctly even when no published content exists:
export default function Page({ page }: { page: BuilderContent | null }) {
const isPreview = useIsPreviewing();
// Show content if it exists or if in preview mode
if (!page && !isPreview) {
return <DefaultErrorPage statusCode={404} />;
}
return <BuilderComponent model={modelName} content={page || undefined} />;
}
For static generation, configure your paths to include fallback handling:
export async function getStaticPaths() {
return {
paths: ["/private-page-example"], // Add your known private page paths
fallback: "blocking", // Enable fallback for new pages
};
};
The BuilderComponent
receives the pre-fetched content from your server-side function. Since the private API key was used server-side, the content is safely passed to the client without exposing sensitive credentials:
export default function Page({ page }: { page: BuilderContent | null }) {
const isPreview = useIsPreviewing();
if (!page && !isPreview) {
return <DefaultErrorPage statusCode={404} />;
}
return (
<>
<BuilderComponent
model={modelName}
content={page || undefined}
/>
</>
);
}
Here's a full implementation for a private model page:
// pages/[[...index]].tsx
import { BuilderComponent, builder, useIsPreviewing } from "@builder.io/react";
import type { BuilderContent } from "@builder.io/sdk";
import type { GetStaticProps } from "next";
import DefaultErrorPage from "next/error";
import React from "react";
builder.init("db60bf3db7fa4db7be81ef05b72bd720");
const modelName = "private-landing-pages";
export const getStaticProps: GetStaticProps = async (opts) => {
let pathName = "/" + ((opts.params?.index as string[])?.join("/") || "");
// Set private key for server-side access only
builder.authToken = process.env.BUILDER_PRIVATE_API_KEY;
const page = await builder
.get(modelName, {
userAttributes: { urlPath: pathName }
})
.promise();
return {
props: { page: page || null },
revalidate: 5,
};
};
export async function getStaticPaths() {
return {
paths: ["/private-landing-page-example"],
fallback: "blocking",
};
}
export default function Page({ page }: { page: BuilderContent | null }) {
const isPreview = useIsPreviewing();
if (!page && !isPreview) {
return <DefaultErrorPage statusCode={404} />;
}
return <BuilderComponent model={modelName} content={page || undefined} />;
}
Now that you have secure private model previewing set up, you can:
- Add custom components to your private models
- Set up targeting and scheduling for your private content
- Configure data bindings for dynamic private content
- Learn about Content API for advanced private content querying