Not able to edit page content when I integrate builder page with local code

Hi,

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:

  1. 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.
  2. the builder preview did show my local page like nextjs default home page, but I keeping has “preview load error”, why?

here is my nextjs code

and [[…page]].tsx/page.tsx code here

// 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} />
    </>
  );
}

and this is builder page model structure:

Please let me know if you need any other information. Thank you.

Hey @Min , if you are naming your pages folder as [[...page]] you have to delete your pages.tsx in the app folder.

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


In the code above, I already changed modal name to “home_page” as shown in builder modal.

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} />
    </>
  );
}

I updated the code as below, add use client and disable ssr:

'use client';
import { useEffect, useState } from 'react';
import { BuilderComponent, builder, useIsPreviewing } from '@builder.io/react';
import dynamic from 'next/dynamic';

// Put your API key here
builder.init('e1bc2675c6de443ea46df24d25d0b6b0');
const CatchAllRoute = () => {
  const isPreviewingInBuilder = useIsPreviewing();
  const [notFound, setNotFound] = useState(false);
  const [content, setContent] = useState(null);

  // get the page content from Builder
  useEffect(() => {
    console.log('content');
    if (typeof window !== 'undefined') {
      console.log('content test');
      async function fetchContent() {
        try {
          const content = await builder
            .get('about', {
              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();
    }
  }, []);

  // If no page is found, return
  // a 404 page from your code.
  // The following hypothetical
  // <FourOhFour> is placeholder.
  if (notFound && !isPreviewingInBuilder) {
    return <div>page not found</div>;
  }

  // return the page when found
  return (
    <>
      {/* Render the Builder page */}
      <BuilderComponent model="about" content={content ?? undefined} />
    </>
  );
};

export default dynamic(() => Promise.resolve(CatchAllRoute), { ssr: false });

But the page remains 404, and I put some debug info in useEffect. It turns out none of them dispaly in console.

And instead of reaching my 404 placeholder:
if (notFound && !isPreviewingInBuilder) {
return

page not found
;
}
the 404 page was actually rendered from ssr response

Hi Min,

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 very much.

1 Like

Hi Veronika,

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.