Symbol bind reference to field SSR

Hi!

I have many symbols, which are used with multiple data models, which are bind as reference in to my symbol:

Here is my symbol, and as you can see the state holds my data:
image

And with this id, I will fetch my data from the API in the custom JS section:

  * for more information on writing custom code
  */
 async function main () {
  if (Builder.isServer) {
      let id=state.hero.id

  fetch('https://cdn.builder.io/api/v2/content/table-data?apiKey=32f148657e2646be8562eb4e6ebfa190&query.id=' +id + '&limit=1&includeRefs=true')
  .then(response => response.json())
  .then(data => state.result=data);
  }
}

export default main();

But because I don’t want to lose SSR, i put that into the if(Builder.isServer), but it does not work, and the data won’t be fetched.

If I modify for browser it works and the state holds my data:

 async function main () {
  if (Builder.isServer || Builder.isBrowser) {
      let id=state.hero.id

  fetch('https://cdn.builder.io/api/v2/content/table-data?apiKey=32f148657e2646be8562eb4e6ebfa190&query.id=' +id + '&limit=1&includeRefs=true')
  .then(response => response.json())
  .then(data => state.result=data);
  }
}

export default main();

We use Nextjs for the site SSR, we fetch all the pages we need, so the data on the page should be ready when we open the browser
Then on the page it looks like it is fetching the data (in the symbol) before rendering it:

After the symbol loaded the data:

(Btw the navbar is loaded from nextjs code, and the logos are custom react components, these are not symbols)

Why is my data not rendered on the SSR, why we need to add the (|| Builder.isBrowser)?
Which is pretty bad for seo optimalisation and performance issues.

Please let me know, because this is a crucial point in our production app.

Hey @radikris I think we actually made an update recently that will make it so you no longer have to do a separate API call in your JS window to receive reference data.

If you add options={{ includeRefs: true }} to your <BuilderComponent> and also inside your builder.get calls, you should receive the reference data directly in state without needing another API call in the custom JS window.

So in your get props calls you would do something like:

const data = await builder.get(modelName, { includeRefs: true, ...options })

and then also in your

<BuilderComponent content={data } options={{ includeRefs: true}} /> 

Try those out, I think it will give you a cleaner, more reliable result without needing any additional API calls or custom JS updates.

Hi!

This is the code how I render my pages:

import { useRouter } from 'next/router'
import { BuilderComponent, Builder, builder } from '@builder.io/react'
import DefaultErrorPage from 'next/error'
import Head from 'next/head'
import builderConfig from '@config/builder'
import '@builder.io/widgets'
import NavBar from '@components/Navbar/NavBar'
import { Helmet } from 'react-helmet'

...

export async function getStaticProps({ params, locale }) {
  const page =
    (await builder
      .get('page', {
        options: { includeRefs: true },
        userAttributes: {
          urlPath: '/' + (params?.page?.join('/') || ''),
        },
      })
      .toPromise()) || null

 ...

  return {
    props: {
      page,
    },
    revalidate: 5,
  }
}

export async function getStaticPaths() {
  const pages = await builder.getAll('page', {
    options: { noTargeting: true, includeRefs: true },
    omit: 'data.blocks',
  })

  return {
    paths: pages.map((page) => `${page.data?.url}`),
    fallback: true,
  }
}

export default function Page({ page, navbar, footer, subfooter }) {
  const seo = page?.data?.seotag ?? null
  const router = useRouter()
  if (router.isFallback) {
    return <h1>Loading...</h1>
  }
  const isLive = !Builder.isEditing && !Builder.isPreviewing
  if (!page && isLive) {
    return (
      <>
        <Head>
          <meta name="robots" content="noindex" />
          <meta name="title"></meta>
        </Head>
        <Error404 />
      </>
    )
  }

  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </Head>
      <NextSeo
       ...seotag
      />
    
      <BuilderComponent
        model="page"
        content={page}
        options={{ includeRefs: true }}
      />
    </>
  )
}

So I only fetch the pages, and I drag the symbols in the visual editor to it. So maybe I did not understand something correctly, but this would be specific for a symbol if I want to retrieve it from my code, but these symbols are on the page itself, and I want to get the whole page.

(But the example I added show you, that somehow I have to retrieve the data with the reference’s id, otherwise I can’t bind my data dynamically to my symbol)
Here is a symbol example: Builder.io: Drag and drop page builder and CMS

We use this symbol with multiple data entries in our pages, and we will bind the reference to it on the page itself, that’s why we added the custom js to fetch the data by id: How to bind a reference field to a symbol)
So maybe I don’t understand you correctly, but how could I avoid this extra fetch so my page will render SSR?

Thank you for your help.

Ah @radikris, try adding noTraverse: false to your options, as seen here: Content API Docs

I think I missed including that as well in my earlier response. Try it out and see if that fixes the issue for you!

Hi @TimG !

I added these options, but still not satisfied with the result, (or not even helped, idk)

Full code: [[…page]]].jsx

import { useRouter } from 'next/router'
import { BuilderComponent, Builder, builder } from '@builder.io/react'
import DefaultErrorPage from 'next/error'
import Head from 'next/head'
import builderConfig from '@config/builder'
import '@builder.io/widgets'

export async function getStaticProps({ params, locale }) {
  const page =
    (await builder
      .get('page', {
        options: { includeRefs: true, noTraverse: false },
        userAttributes: {
          urlPath: '/' + (params?.page?.join('/') || ''),
        },
      })
      .toPromise()) || null

  let navbar =
    (await builder
      .get('navigation', QUERY({ ...LOCALE_FILTER(locale), id: 'navbar' }))
      .promise()) || null
  let footer =
    (await builder
      .get('navigation', QUERY({ ...LOCALE_FILTER(locale), id: 'footer' }))
      .promise()) || null
  let subfooter =
    (await builder
      .get('navigation', QUERY({ ...LOCALE_FILTER(locale), id: 'subfooter' }))
      .promise()) || null

  return {
    props: {
      page,
      navbar,
      footer,
      subfooter,
    },
    revalidate: 5,
  }
}

export async function getStaticPaths() {
  const pages = await builder.getAll('page', {
    options: { noTargeting: true, includeRefs: true, noTraverse: false },
    omit: 'data.blocks',
  })

  return {
    paths: pages.map((page) => `${page.data?.url}`),
    fallback: true,
  }
}

export default function Page({ page, navbar, footer, subfooter }) {
  const seo = page?.data?.seotag ?? null
  const router = useRouter()
  if (router.isFallback) {
    return <h1>Loading...</h1>
  }
  const isLive = !Builder.isEditing && !Builder.isPreviewing
  if (!page && isLive) {
    return (
      <>
        <Head>
          <meta name="robots" content="noindex" />
          <meta name="title"></meta>
        </Head>

        <BuilderComponent model="navigation" content={navbar} />

        <Error404 />
      </>
    )
  }

  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </Head>
      <NextSeo .../>
      <BuilderComponent model="navigation" content={navbar} />
      <BuilderComponent
        model="page"
        content={page}
        options={{ includeRefs: true, noTraverse: false }}
      />
      <BuilderComponent model="navigation" content={footer} />
      <BuilderComponent model="navigation" content={subfooter} />
    </>
  )
}

I added everywhere the notraverse option, and the result:

As you can see, all of the symbols are loading later, the custom react components (like the logos, and basic drag and drop elements, sections are already there, symbols only later)
Here is the home link: Builder.io: Drag and drop page builder and CMS

Please help me resolve this issue!

Hey @radikris what is logging if you console log page within your Page component? In the place where there should be your symbols specifically? I am trying to see if noTraverse and includeRefs being honored and passed correctly

I think it has to do with the fact that you are fetching the Symbol data inside the Symbol custom JS window…if you have the includeRefs in all your <BuilderComponent/>s and builder.get() calls then you shouldn’t actually need that I don’t think as you are fetching the data that should be coming in the reference data correct?

Here is the log:

{
  createdBy: 'uw0UJ7VD7hS21zDPkekcB9zZgAD3',
  createdDate: 1645170188427,
  data: {
    customFonts: [ [Object], [Object], [Object] ],
    httpRequests: {},
    inputs: [],
    title: '<p>home</p>',
    blocks: [
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object]
    ],
    url: '/',
    state: { deviceSize: 'large', location: [Object], cookies: [Object] }
  },
  firstPublished: 1643796410477,
  id: '08b967c8b57b40e99bccb3ffe9497299',
  lastUpdated: 1648130349486,
  lastUpdatedBy: 'XqRxT5eLULcOhmuGd9uakTM61uX2',
  meta: {
    hasLinks: false,
    kind: 'page',
    originalContentId: '7301652a6bfa4bfca658e8b442bdfa4a',
    winningTest: null,
    needsHydration: true
  },
  modelId: '48b6ed85139d4aeb8f8fb068f29d01b9',
  name: 'home',
  published: 'published',
  query: [
    {
      '@type': '@builder.io/core:Query',
      operator: 'is',
      property: 'urlPath',
      value: '/'
    }
  ],
  screenshot: 'https://cdn.builder.io/api/v1/image/assets%2F32f148657e2646be8562eb4e6ebfa190%2Fd8e61580f79e4beea9d2a9743d2340f7',
  testRatio: 1,
  variations: {},
  rev: 'zf208m5fq79'
}
Warning: data for page "/[[...page]]" is 215 kB, this amount of data can reduce performance.
See more info here: https://nextjs.org/docs/messages/large-page-data
undefined
undefined
undefined
{
  createdBy: 'uw0UJ7VD7hS21zDPkekcB9zZgAD3',
  createdDate: 1645170188427,
  data: {
    customFonts: [ [Object], [Object], [Object] ],
    httpRequests: {},
    inputs: [],
    title: '<p>home</p>',
    blocks: [
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object]
    ],
    url: '/',
    state: { deviceSize: 'large', location: [Object], cookies: [Object] }
  },
  firstPublished: 1643796410477,
  id: '08b967c8b57b40e99bccb3ffe9497299',
  lastUpdated: 1648130349486,
  lastUpdatedBy: 'XqRxT5eLULcOhmuGd9uakTM61uX2',
  meta: {
    hasLinks: false,
    kind: 'page',
    originalContentId: '7301652a6bfa4bfca658e8b442bdfa4a',
    winningTest: null,
    needsHydration: true
  },
  modelId: '48b6ed85139d4aeb8f8fb068f29d01b9',
  name: 'home',
  published: 'published',
  query: [
    {
      '@type': '@builder.io/core:Query',
      operator: 'is',
      property: 'urlPath',
      value: '/'
    }
  ],
  screenshot: 'https://cdn.builder.io/api/v1/image/assets%2F32f148657e2646be8562eb4e6ebfa190%2Fd8e61580f79e4beea9d2a9743d2340f7',
  testRatio: 1,
  variations: {},
  rev: 'wbcho3j8yo8'
}


So the symbol is there, but how could I avoid the extra fetch in my symbol?

Here is a symbol: Builder.io: Drag and drop page builder and CMS

If I don’t fetch the data by id, I can’t bind the fields?


I save it to the state.result:
image

All of my fields are bind with the state.result. If I don’t fetch the extra data and save it to ‘result’ field, my symbol won’t know what fields do I want to bind to it, am I right?

Hey @radikris so I went ahead and created a copy of the symbol for testing, we can delete later once this is solved: Builder.io: Drag and drop page builder and CMS

If you notice, when I log the reference data, it seems to be coming in in the preview and live pages but not the editing page. I can look further into why that is, possibly an issue on our end…but we can also account for that using the Builder.isEditing flag and use the fetch method when editing and the reference data in. preview/live sites. Check the code I put in the custom JS window:

 async function main () {
  if (Builder.isServer || Builder.isBrowser) {
    let id=state.hero.id;

    if (Builder.isEditing) {
      fetch('https://cdn.builder.io/api/v2/content/table-data?apiKey=32f148657e2646be8562eb4e6ebfa190&query.id=' +id + '&limit=1&includeRefs=true')
      .then(response => response.json())
      .then(data => { 
        state.result=data
        console.log('IS EDITING DATA: ', state);
      });

    } else {
      console.log(state.hero.value);
      state.result = { results: [state.hero.value] };
      console.log('LIVE DATA: ', state);
    }
 }
}

export default main();

See if this works for you , it wont require you to change any of your bindings since instead I am setting the reference data in a similar object. Thoughts?

Hi @TimG!

Yes, it looks like it is working, I just changed my other symbol with your approach,
see here:

And I tried with refresh the page 10-15x, and I don’t know what caused, but I could achieve that it failed,

And did not load any of the symbols.
Although after some new refresh its back again:

Although the console shows the content:

It is very strange, I thought you solved it, but looks like it is still buggy. Can you please check this out?
https://2022-barion-com.vercel.app/hu/ if you want to try (must push the refresh, or ctrl+shift+r a lot to see this bug)

Hey @radikris are you still having this issue? I wasn’t able to re-create it on my end on the live homepage. Hopefully it is resolved?

No it is not.

You should refresh the page many time to recreate this bug.

And I think it is a Builder.io bug

Hey @radikris I do see that now after a large number of refreshes. I have raised a bug with our dev team to investigate. I am hoping it is an edge case and few users should ever encounter it, but please let us know if you start to have more issues as a result of this.

I am not sure it is an edgecase
https://2022-barion-com.vercel.app/hu/integrations-and-plugins/

here if you scroll down, and try to refresh many times, these symbols are heroes but fetched from code, and the results are different (but both of them never seems to be visible at the same time):




This is the way we fetch from code:

  let integrationready =
    (await builder
      .get('buildercomponents', {
        options: {
          includeRefs: true,
          noTraverse: false,
          query: {
            data: { ...LOCALE_FILTER(locale), id: 'ready' },
          },
        },
      })
      .promise()) || null

  let integrationrunning =
    (await builder
      .get('buildercomponents', {
        options: {
          includeRefs: true,
          noTraverse: false,
          query: {
            data: { ...LOCALE_FILTER(locale), id: 'running' },
          },
        },
      })
      .promise()) || null

And for the display:

     <BuilderComponent
          content={integrationrunning}
          model="buildercomponents"
          options={{ includeRefs: true, noTraverse: false }}
        />
        <BuilderComponent
          content={integrationready}
          model="buildercomponents"
          options={{ includeRefs: true, noTraverse: false }}
        />

This is pretty bad, especially this flickering, you can see the symbol is fetched, but after that it disappears:

Please check out this bug as soon as possible, we cannot release a production app with this issue, and we are paying the growth plan, and we think as a growth plan customer, maybe you can push the urgency of this ticket to your dev team.

Best regards,
Kristof

Hey @radikris I am no longer able to reproduce this at all:
Homepage: Loom | Free Screen & Video Recording Software
Integrations page: Loom | Free Screen & Video Recording Software

I spent 2 minutes on both pages constantly refreshing. I tested both on Chrome and Safari, did yall make any changes? I totally understand that having a page missing content would be a no-go, but when I said edge case I just meant that very few users will be constantly refreshing a page over and over, so I think it is an unlikely use case ‘in the wild’.

Also, as to flashing on loading, one way to fix this is to wrap the section in a data binding at put something like showIf and then if there is data or a state you are waiting on put the value as that data.content or state.results or whatever the case may be

Let me know if that helps…if you continue to have the issue we can try to dig deeper

Hi TimG!

No, we did not make any drastic changes, but currently I refreshed a lot, and also I did not reproduce the issue.
I will now try to refactor all of my symbols with your approach, and we will see if the issue is coming back or not.

Thank you for your help, I will reply to this forum post if I notice something.

@TimG and I am back!

So, I just replaced all of my symbols with this Builder.isEditing approach, and here is the result loom:

It is pretty strange, now the customgrid symbol works buggy:
Link the customgrid symbol: Builder.io: Visual Development Platform

Please check this out, and inform your dev team about this issue, something is breaking on your side.
And

the flashing on loading
I think you misunderstod me, I don’t think of the loading progress, but the ‘showing and disappearing’ issue which you can see at: 00:19 , the grid loads and is visible for a very short tim, but after that immediately it disappears)

I think now you and your team have a great input on this issue, and you can improve Builder.io to be more efficient and better.

Thank you very much in advance!
Best wishes,
Kristof

Ok @radikris I did some more digging, I havent fully found WHY the content was coming blank, but I figured out how to STOP it from happening. It is related to this code:

For some reason, even though state.hero.value is always coming in the data, it is failing to be set to state.result and so that is sometimes being set to null. So, I have updated the logic in the customgrid symbol to say

if (!state.hero.value) {
      fetch('https://cdn.builder.io/api/v2/content/custom-grid-data?apiKey=32f148657e2646be8562eb4e6ebfa190&query.id=' +id + '&limit=1&includeRefs=true')
      .then(response => response.json())
      .then(data => { 
        state.result=data
        //console.log('IS EDITING DATA: ', state);
      });

    } else {
      state.result = { results: [state.hero.value] };
    }

So instead of checking for Builder.isEditing we are checking if there is no data from the references model (state.hero.value), then we should do the manual API call. If not just set it to the referenced data.

Try that out and see if that works for you…you may need to update the other Symbols to this new logic to make sure if doesnt happen elsewhere.

I am going to continue digging into this, I have a hunch there is some issue in either our SDK or our data window, but it is very hard to pinpoint exactly where…the good news is that at least for now you shouldn’t have any negative user experience. I will keep you posted as I find anything else

1 Like

Thank you very much for getting back to me as you promised. I replaced all of my symbols with this approach, and looks promising, if I will notice anything I will let you know, but currently this looks working properly.
Have a nice day!

1 Like

@TimG hey, I think some of your latest update changed this.
Earlier it was working properly, but currently it looks fishy.

Check the video and you will see, that only the codecomponents and visual editor fields are loading at the first time, and after that coming the symbol with the reference fields.

At the end of the video you will see the reference content in the symbol, but that’s not the rendered html you would like to see in an SSR page.

Can you please check this out, and maybe the symbol as well, link: Builder.io: Drag and drop page builder and CMS. (The same is true for all of my symbols)