Get image dimensions from api before the acual images

The whole code structure is a bit complex because I do a whole bunch of other trickery this way during the build. I hope these few snippets can get you going (I’m omitting a few specifics, import statements, and type annotations etc.).

There are basically 4 files involved — the server-rendered page.tsx that calls the builder API and extractData; the client component that renders builder content inside the context provider; the context file (separated so it can be cleanly imported); and the extractData function itself.

// [[...path]]/page.tsx
builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!)

export default async function Page ({ params: { path } }) {
  const pageContent = await builder.get('page', {/* settings */})
  const extractedData = pageContent ? await extractData(pageContent) : {}
  /* ...more site-specific setup */

  return (  
    <RenderBuilderContent content={pageContent}
                          extractedData={extractedData}
                          model="page"
    />
  )
}
// RenderBuilderContent.ts
// in it's own file because the BuilderComponent needs 'use client'
'use client'

export function RenderBuilderContent ({ content, extractedData, model }) {
  /* ...more site-specific setup */
  
  return (
    <ExtractedDataContext.Provider value={extractedData}>
      <BuilderComponent
          content={content}
          model={model}
      />
    </ExtractedDataContext.Provider>
  )
}
// ExtractedDataContext.ts
import { createContext } from 'react'

export default createContext<Record<string, any>>({})
// extractData.ts

// get the value of a key from a JSON string
// this could theoretically also be achieved with a walker function, but since builder blocks have a well-known shape this is fine
const valueForKeyRegExp = (key: string) => new RegExp(`("${key}":\\s?")([^"]+)(")`, 'gm')

export default async function extractData (page) {
  const extractedData = {
    images: {},
    priorityAssets: [],
    /* other stuff we want to get */
  }

  async function getImageData ({ url }) {
    try {
      // pull in image data directly from the builder API endpoint
      const result = await require('probe-image-size')(url, { open_timeout: 30000, follow_max: 10 })
      // and store them using the URL as the key
      extractedData.images[url] = { ...result }
    } catch (e) {
      console.warn(e)
    }
  }

  const blocks = page?.data?.blocks
  for (const [i, block] of (blocks ?? []).entries()) {
    const text = JSON.stringify(block)
    for (const key of ['image', 'poster', /* ...any other keys used for images */]) {
      const matches = text.matchAll(valueForKeyRegExp(key))
      for (const match of matches) {
        const url = match[2]
        if (url) {
          await getImageData({ url })
          // get assets from the first 2 blocks and mark them for fetchpriority
          if (i <= 2) {
            extractedData.priorityAssets.push(url)
          }
        }
      }
    }
  }

  return extractedData
}

I’ve been thinking that I could also just append the extractedData to the API response itself (i.e. the content object) instead of using a Context, but haven’t gotten around to testing this.

Hope this helps!