I created a new nextjs project with builder command: npm init builder.io@latest in my local environment.
And connect local code to builder successfully. When I tried to integrate my local page with builder created page, there are some error here:
the page content is newly created, and there is no option to edit the page. I watched demo video, there should be some basic components to be added into page preview section or “add a block” button.
// pages/[...page].tsx
import React from 'react';
import { useRouter } from 'next/router';
import { BuilderComponent, builder, useIsPreviewing } from '@builder.io/react';
import { BuilderContent } from '@builder.io/sdk';
import DefaultErrorPage from 'next/error';
import Head from 'next/head';
import { GetStaticProps } from 'next';
// Replace with your Public API Key
builder.init('hide my api key');
// Define a function that fetches the Builder
// content for a given page
export const getStaticProps: GetStaticProps = async ({ params }) => {
// Fetch the builder content for the given page
const page = await builder
.get('page', {
userAttributes: {
urlPath: '/' + ((params?.page as string[])?.join('/') || ''),
},
})
.toPromise();
// Return the page content as props
return {
props: {
page: page || null,
},
// Revalidate the content every 5 seconds
revalidate: 5,
};
};
// Define a function that generates the
// static paths for all pages in Builder
export async function getStaticPaths() {
// Get a list of all pages in Builder
const pages = await builder.getAll('page', {
// We only need the URL field
fields: 'data.url',
options: { noTargeting: true },
});
// Generate the static paths for all pages in Builder
return {
paths: pages.map((page) => `${page.data?.url}`).filter((url) => url !== '/'),
fallback: 'blocking',
};
}
// Define the Page component
export default function Page({ page }: { page: BuilderContent | null }) {
const router = useRouter();
const isPreviewing = useIsPreviewing();
// If the page content is not available
// and not in preview mode, show a 404 error page
if (!page && !isPreviewing) {
return <DefaultErrorPage statusCode={404} />;
}
// If the page content is available, render
// the BuilderComponent with the page content
return (
<>
<Head>
<title>{page?.data?.title}</title>
</Head>
{/* Render the Builder page */}
<BuilderComponent model="page" content={page || undefined} />
</>
);
}
One more issue I could see is you are using getStaticProps with the App router. getStaticProps doesn’t work with the App router.
You can use the following code with the App router -
// Example file structure, app/[...page]/page.tsx
// You could alternatively use src/app/[...page]/page.tsx
import { builder } from "@builder.io/sdk";
import { RenderBuilderContent } from "../../components/builder";
import React from "react";
// Replace with your Public API Key
builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!);
interface PageProps {
params: {
page: string[];
};
}
export default async function Page(props: PageProps) {
//const model = "page";
const content = await builder
// Get the page content from Builder with the specified options
.get("page", {
userAttributes: {
// Use the page path specified in the URL to fetch the content
urlPath: "/" + (props?.params?.page?.join("/") || ""),
},
// Set prerender to false to return JSON instead of HTML
prerender: false,
})
// Convert the result to a promise
.toPromise();
return (
<>
{/* Render the Builder page */}
<RenderBuilderContent content={content} model={"page"} />
</>
);
}
I removed extra page.tsx file and updated [[…page]]/page.tsx code like this:
import { useEffect, useState } from 'react';
import { BuilderComponent, builder, useIsPreviewing } from '@builder.io/react';
// Put your API key here
builder.init("api key here");
// set whether you're using the Visual Editor,
// whether there are changes,
// and render the content if found
export default function CatchAllRoute() {
const isPreviewingInBuilder = useIsPreviewing();
const [notFound, setNotFound] = useState(false);
const [content, setContent] = useState(null);
// get the page content from Builder
useEffect(() => {
async function fetchContent() {
const content = await builder
.get('home_page', {
url: window.location.pathname,
})
.promise();
setContent(content);
setNotFound(!content);
// if the page title is found,
// set the document title
if (content?.data.title) {
document.title = content.data.title;
}
}
fetchContent();
}, [window.location.pathname]);
// If no page is found, return
// a 404 page from your code.
// The following hypothetical
// <FourOhFour> is placeholder.
// if (notFound && !isPreviewingInBuilder) {
// return <FourOhFour/>
// }
// return the page when found
console.log('content', content);
return (
<>
{/* Render the Builder page */}
<BuilderComponent model="home_page" content={content ?? undefined} />
</>
);
}
But the builder page still not editable and there is 404 in localhost
Hey, @Min, you are using state with server-side components. There are a few changes you would need to make in your code for this to work.
"use client";
import { useEffect, useState } from 'react';
import { BuilderComponent, builder, useIsPreviewing } from '@builder.io/react';
// Initialize Builder.io with your API key
builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!);
// Main component
export default function CatchAllRoute() {
const isPreviewingInBuilder = useIsPreviewing();
const [notFound, setNotFound] = useState(false);
const [content, setContent] = useState(null);
// Fetch the page content from Builder
useEffect(() => {
// Check if running on the client-side
if (typeof window !== 'undefined') {
async function fetchContent() {
try {
const content = await builder.get('home_page', {
url: window.location.pathname,
}).promise();
setContent(content);
setNotFound(!content);
// Set the document title if content has a title
if (content?.data.title) {
document.title = content.data.title;
}
} catch (error) {
console.error('Error fetching content:', error);
}
}
fetchContent();
}
}, []); // Removed `window.location.pathname` from dependency array
// Return a 404 page if content is not found and not in preview mode
if (notFound && !isPreviewingInBuilder) {
return <div>Page Not Found</div>;
}
// Render the Builder page content
return (
<>
<BuilderComponent model="home_page" content={content ?? undefined} />
</>
);
}
It looks like you might be using the Next code instead of the App Router Next.js code.
I went ahead and took a loom video to help further debug the issue you’re running into. It can be found here.
If you go ahead into your model, click into ‘fields’, you should see something that says ‘Developer Usage’ if you scroll down. Here you can see all the frameworks that we integrate with, click on ‘App Router’ and give that code a shot. I’ve also added step by step instructions in the video as well.
Also, as Sheema said, please make sure your unique identifier name (model name) corresponds to the name in your code. Please also make sure you have the preview URL set to your localhost and have npm run dev running.
Please let me know if this works and if you have any further questions,
Thank you so much for the video and now my localhost can successfully integrate with builder page. Just curious, why my previous client side render codes not work along with builder?
What is the difference between next code (page route) and app route code ? My understanding is the way nextjs organizing route. Why it will affect builder page integration?
Another question is, the code builder.get() method take one model name as parameter to render builder page, what if I want it to render multiple model in one […page].tsx page? so I can dynamically render different page model based on certain conditon.