#What is the correct way to set default props when using the builder CMS?

4 messages · Page 1 of 1 (latest)

lofty fossil
#

Here's a simple hero component. It properly populates the slotted content, but not the properties of the component itself. What's the correct way to set default properties but favor the content from the Builder CMS?

import { component$, useStyles$, Slot } from '@builder.io/qwik';
import styles from './hero.css?inline';

export interface HeroProps {
  image: {
    url: string;
    alt: string;
  }
}

export const defaultProps: HeroProps = {
  image: {
    url: "https://cdn.builder.io/api/v1/image/assets%2F37ded3b122524a739392c33ac92e0db5%2F33553de4c41a4cfd9176a91f5f907b6f",
    alt: "A random image from picsum.photos",
  }
}

const Hero = component$<HeroProps>(({image = defaultProps.image}) => {
  useStyles$(styles);

  return (
    <section class="hero">
      <img class="hero__image" src={image.url} alt={image.alt}/>
      <div class="hero__mask"></div>
      <div class="hero__content">
        <Slot></Slot>
      </div>
    </section>
  );
});

export default Hero;
#

This is the entry in the builder-registry.ts in case my mistake is in there:

  {
    component: Hero,
    name: "Hero",
    inputs: [
      {
        name: "Image",
        type: "file",
        allowedFileTypes: ['jpeg', 'jpg', 'png', 'svg'],
        required: true,
        defaultValue: 'https://picsum.photos/1360/907',
      },
      {
        name: "Image Alt",
        type: "string",
        defaultValue: 'Hero Image Description',
      },
    ],
    defaultChildren: [
      { 
        '@type': '@builder.io/sdk:Element',
        component: { name: 'Text', options: { text: 'I am child text block!' } }
      }
    ]
  },
lofty fossil
#

This is the routes/[...index]/index.tsx file. I think it's unchanged from the starter template. (removed comments so it'd fit)

import { component$ } from "@builder.io/qwik";
import type { DocumentHead} from "@builder.io/qwik-city";
import { routeLoader$ } from "@builder.io/qwik-city";
import {
  getContent,
  RenderContent,
  getBuilderSearchParams,
} from "@builder.io/sdk-qwik";
import { CUSTOM_COMPONENTS } from "../../components/builder-registry";

export const useBuilderContent = routeLoader$(async ({ url, error }) => {
  const isPreviewing = url.searchParams.has("builder.preview");

  const builderContent = await getContent({
    model: "page",
    apiKey: import.meta.env.PUBLIC_BUILDER_API_KEY,
    options: getBuilderSearchParams(url.searchParams),
    userAttributes: {
      urlPath: url.pathname,
    },
  });

  if (!builderContent && !isPreviewing) {
    throw error(404, "Page not found");
  }

  return builderContent;
});

export default component$(() => {
  const builderContent = useBuilderContent();

  return (
    <div>
    <RenderContent
      model="page"
      content={builderContent.value}
      apiKey={import.meta.env.PUBLIC_BUILDER_API_KEY}
      customComponents={CUSTOM_COMPONENTS}
    />
    </div>
  );
});

export const head: DocumentHead = ({ resolveValue }) => {
  const builderContent = resolveValue(useBuilderContent);
  return {
    title: builderContent?.data?.title,
  };
};
lofty fossil
#

Figured it out! The name value needs to match the prop spelling exactly. If I want Image to be an object, I need to wrap these properties in the builder registry like this:

  {
    component: Hero,
    name: "Hero",
    inputs: [
      {
        name: "image",
        type: "object",
        defaultValue: {
          url: 'https://picsum.photos/1360/907',
          alt: 'Hero Image Description',
        },
        subFields: [
          {
            name: "url",
            type: "file",
            allowedFileTypes: ['jpeg', 'jpg', 'png', 'svg'],
            required: true,
            defaultValue: 'https://picsum.photos/1360/907',
          },
          {
            name: "alt",
            type: "string",
            defaultValue: 'Hero Image Description',
          },
        ]
      }
    ],
    defaultChildren: [
      { 
        '@type': '@builder.io/sdk:Element',
        component: { name: 'Text', options: { text: 'I am child text block!' } }
      }
    ]
  },