createRender

createRender lets you define complex Svelte component behaviors within the script.

It combines a component with its props to create a ComponentRenderConfig that is passed into Render#of to dynamically render Svelte components.

createRender accepts a Svelte component and either:

  1. an object of static props; or
  2. a getter returning the props, for dynamic props.

Event handlers are ordinary props: pass them as on<event> (for example onclick), the same way you would on any Svelte 5 component.

Usage


createRender: (component: Component, props?: Props) => ComponentRenderConfig

Creates a render configuration with a Svelte component and static props.

<script>
    import Profile from './Profile.svelte'
    const profile = createRender(Profile, {
        name: 'Alan Turing'
    })
</script>

<Render of={profile} />
<script>
    import Profile from './Profile.svelte'
    const profile = createRender(Profile, {
        name: 'Alan Turing'
    })
</script>

<Render of={profile} />

Props may themselves be reactive objects. The plugin state boxes are the common case: pass the box, and let the component read and write its current.

cell: ({ row }, { pluginStates }) => {
    const { isSelected, isSomeSubRowsSelected } = pluginStates.select.getRowState(row)
    return createRender(SelectIndicator, { isSelected, isSomeSubRowsSelected })
}
cell: ({ row }, { pluginStates }) => {
    const { isSelected, isSomeSubRowsSelected } = pluginStates.select.getRowState(row)
    return createRender(SelectIndicator, { isSelected, isSomeSubRowsSelected })
}
<!-- SelectIndicator.svelte -->
<script lang="ts">
    import type { Box, ReadonlyBox } from '@humanspeak/svelte-headless-table'

    const { isSelected, isSomeSubRowsSelected }: {
        isSelected: Box<boolean>
        isSomeSubRowsSelected: ReadonlyBox<boolean>
    } = $props()
</script>

<input
    type="checkbox"
    checked={isSelected.current}
    indeterminate={isSomeSubRowsSelected.current}
    onchange={(e) => (isSelected.current = e.currentTarget.checked)}
/>
<!-- SelectIndicator.svelte -->
<script lang="ts">
    import type { Box, ReadonlyBox } from '@humanspeak/svelte-headless-table'

    const { isSelected, isSomeSubRowsSelected }: {
        isSelected: Box<boolean>
        isSomeSubRowsSelected: ReadonlyBox<boolean>
    } = $props()
</script>

<input
    type="checkbox"
    checked={isSelected.current}
    indeterminate={isSomeSubRowsSelected.current}
    onchange={(e) => (isSelected.current = e.currentTarget.checked)}
/>

createRender: (component: Component, props?: () => Props) => ComponentRenderConfig

Creates a render configuration with a Svelte component and dynamic props. <Render> calls the getter inside its template, so the component re-renders with new props when the rune state the getter reads changes.

<script>
    import Profile from './Profile.svelte'
    let name = $state('Grace Hopper')
    const profile = createRender(Profile, () => ({ name }))
</script>

<input bind:value={name} />
<Render of={profile} />
<script>
    import Profile from './Profile.svelte'
    let name = $state('Grace Hopper')
    const profile = createRender(Profile, () => ({ name }))
</script>

<input bind:value={name} />
<Render of={profile} />

Event handlers

Pass handlers as on<event> props:

<script>
    import Notice from './Notice.svelte'
    let count = $state(0)
    const notice = createRender(Notice, () => ({
        count,
        onclick: () => count++
    }))
</script>

<Render of={notice} />
<script>
    import Notice from './Notice.svelte'
    let count = $state(0)
    const notice = createRender(Notice, () => ({
        count,
        onclick: () => count++
    }))
</script>

<Render of={notice} />
Information:
v7 removed ComponentRenderConfig#on() and eventHandlers. Replace .on('click', handler) with an onclick prop.

createSnippetRender: (snippet: Snippet<[Args]>, args?: Args | (() => Args)) => SnippetRenderConfig

Creates a render configuration from a Svelte 5 snippet instead of a component. The snippet receives a single argument, either a static value or a getter returning it (the snippet re-renders when the rune state the getter reads changes). Omit args for snippets that take no argument.

Warning:
A function passed as args is always called as a getter. To pass a function as the snippet's argument, wrap it: createSnippetRender(snippet, () => fn).

This works from <script> because Svelte hoists top-level snippets, so a snippet declared in the component’s markup can be referenced while defining columns.

<script>
    import { createSnippetRender } from '@humanspeak/svelte-headless-table'

    const columns = table.createColumns([
        table.column({
            accessor: 'name',
            header: 'Name',
            cell: ({ value }) => createSnippetRender(nameCell, value)
        })
    ])
</script>

{#snippet nameCell(name)}
    <strong>{name}</strong>
{/snippet}
<script>
    import { createSnippetRender } from '@humanspeak/svelte-headless-table'

    const columns = table.createColumns([
        table.column({
            accessor: 'name',
            header: 'Name',
            cell: ({ value }) => createSnippetRender(nameCell, value)
        })
    ])
</script>

{#snippet nameCell(name)}
    <strong>{name}</strong>
{/snippet}