How to pass props to a slot for a custom component in Svelte?

I am trying to access and pass down props in Svelte to a child. I want to do this so I can easily edit the child component in preview. I cannot figure how to do this. The section custom component takes children, in particular a list. This is especially an issue since I am using noWrap

My base page component

<script>
	import { BUILDER_PUBLIC_API_KEY } from '$lib/publicAPIKey';
	import { CUSTOM_COMPONENTS } from '$lib/builderCustomComponents';
	import { Content } from '@builder.io/sdk-svelte';

	export let contentData; // this is coming from GraphQL
</script>

<div class="u-fixed-width">
	<h2 class="p-muted-heading">{contentData.data.secondaryPageTitle}</h2>
	<div class="row">
		<hr class="p-rule" />
	</div>
</div>
<Content
	enrich={true}
	model="page"
	apiKey={BUILDER_PUBLIC_API_KEY}
	content={contentData}
	data={contentData.data}
	customComponents={CUSTOM_COMPONENTS}
/>

builderCustomComponents

import SectionComponent from './SectionComponent.svelte';
import ListComponent from './ListComponent.svelte';

export const CUSTOM_COMPONENTS = [
	{
		component: SectionComponent,
		name: 'Section',
		canHaveChildren: true,
		defaultChildren: [
			{
				component: { name: 'Text', options: { text: 'I am child text block!' } }
			}
		],
		inputs: [
			{
				name: 'title',
				type: 'string',
				required: true,
				defaultValue: 'Title'
			},
			{
				name: 'subTitle',
				type: 'string',
				required: true,
				defaultValue: 'Sub Title'
			}
		]
	},
	{
		component: ListComponent,
		name: 'List',
		noWrap: true,
		inputs: [
			{
				name: 'items',
				type: 'list',
				defaultValue: [{ title: 'This is a bulleted item' }],
				subFields: [
					{
						name: 'title',
						type: 'string',
						required: true,
						defaultValue: '"This is a bulleted item"'
					}
				]
			}
		]
	}
];

The SectionComponent

<script>
	export let title;
	export let subTitle;
</script>

<div class="u-fixed-width">
	<h2 class="p-muted-heading">{subTitle}</h2>
	<div class="row">
		<hr class="p-rule" />
	</div>
</div>
<section class="p-section">
	<div class="row--25-75 is-split-on-medium">
		<div class="col">
			<h3 class="p-heading--1">{title}</h3>
		</div>
		<slot />
	</div>
</section>

ListComponent

<script>
	export let items;
</script>

<div class="col">
	<ul class="p-list--divided">
		{#each items as item}
			<li class="p-list__item has-bullet">
				{item.title}
			</li>
		{/each}
	</ul>
</div>

Hello @cityskylife,

Welcome to the builder.io forum.

Are you looking to pass contentData to custom components? Does it work when using nowrap=false?

For passing props to a custom component, you’ll want to ensure that the data you want to pass to the child component is correctly structured and accessible.

Here’s a basic structure on how you can approach this:

  1. Define Props in Child Component: In your child components (SectionComponent and ListComponent), you’ve already defined props using export let. This is correct.
  2. Pass Props from Parent Component: In your base page component, you should pass the necessary data as props to the child component. This is typically done by setting attributes on the child component that match the exported properties.
  3. Using Builder.io to Manage Props: When using Builder.io, you can create fields in the visual editor that map to these props. This way, when you’re editing the content in Builder.io, you’re effectively setting the values of the props that will be passed to your Svelte component.
  4. Editing Child Component in Preview: To edit the child component in preview within Builder.io, you must ensure that the custom component is correctly registered and its editable fields are properly configured in Builder.io’s visual editor.

Here is an example of how you might structure your parent component to pass props to a child component:

<!-- ParentComponent.svelte -->
<script>
  import ChildComponent from './ChildComponent.svelte';
  let someData = 'Data to pass';
</script>

<ChildComponent propFromParent={someData} />

And in your child component:

svelteCopy code
<!-- ChildComponent.svelte -->
<script>
  export let propFromParent;
</script>

<div>
  The parent says: {propFromParent}
</div>

Not quite, I understand how to pass arbitrary attributes but it seems when noWrap is used Builder looses the necessary class and builder-id attributes to make the component focusable in UI. You can drill into the component using layers. Not a deal breaker but I’d like to fix this.

This is a similar thread but it was addressed in React Custom Component with noWrap "true" cause focus issue - #2 by steve

I figured out the solution, in the parent component (SectionComponent.svelte) I had to expose attributes and pass that into the slot.

<script>
	export let title;
	export let subTitle;
	export let attributes;
</script>

<div class="u-fixed-width">
	<h2 class="p-muted-heading">{subTitle}</h2>
	<div class="row">
		<hr class="p-rule" />
	</div>
</div>
<section class="p-section">
	<div class="row--25-75 is-split-on-medium">
		<div class="col">
			<h3 class="p-heading--1">{title}</h3>
		</div>
		<slot {attributes} />
	</div>
</section>

Then I had ensure my custom class was not overwritten yet adding the Builder attribute and class.

<script>
	export let items;
	export let attributes;
	const formattedData = { id: attributes['builder-id'], class: attributes.class };
</script>

<div builder-id={formattedData.id} class={`col ${formattedData.class}`}>
	<ul class="p-list--divided">
		{#each items as item}
			<li class="p-list__item has-bullet">
				{item.title}
			</li>
		{/each}
	</ul>
</div>

Works perfectly. Hopefully this might help someone else.

1 Like