logo

Snippet Overrides

Snippet overrides are the one deliberate extension the Svelte port adds on top of react-json-view-lite’s API. Pass a Snippet as any of the nine type-keyed props and the viewer calls it instead of the built-in renderer for every matching value.

<script lang="ts">
    import { JsonView } from '@humanspeak/svelte-json-view-lite'
</script>

<JsonView data={payload}>
    {#snippet string({ value })}
        <span class="text-brand-500">{value}</span>
    {/snippet}
</JsonView>
<script lang="ts">
    import { JsonView } from '@humanspeak/svelte-json-view-lite'
</script>

<JsonView data={payload}>
    {#snippet string({ value })}
        <span class="text-brand-500">{value}</span>
    {/snippet}
</JsonView>

Snippet Props

Every value snippet receives the same shape — just with a differently typed value:

interface ValueSnippetProps<T> {
    value: T
    field?: string  // parent key, or undefined at the root / inside arrays
    level: number   // depth (root = 0)
}
interface ValueSnippetProps<T> {
    value: T
    field?: string  // parent key, or undefined at the root / inside arrays
    level: number   // depth (root = 0)
}
SnippetFires forvalue type
stringString leavesstring
numberNumber leavesnumber
booleanBoolean leavesboolean
nullnull leavesnull
undefinedundefined leavesundefined
bigintbigint leavesbigint
dateDate instancesDate
functionFunction valuesFunction
labelEvery field label (key)— ({ field, level })

See Types & snippets for the exported type aliases.

Render URLs as links

<script lang="ts">
    import { JsonView } from '@humanspeak/svelte-json-view-lite'

    const payload = {
        repo: 'https://github.com/humanspeak/svelte-json-view-lite',
        email: 'hello@humanspeak.com'
    }

    const isUrl = (s: string) => /^https?:\/\//.test(s)
</script>

<JsonView data={payload}>
    {#snippet string({ value })}
        {#if isUrl(value)}
            <a href={value} target="_blank" rel="noopener" class="text-brand-500 underline">
                {value}
            </a>
        {:else}
            "{value}"
        {/if}
    {/snippet}
</JsonView>
<script lang="ts">
    import { JsonView } from '@humanspeak/svelte-json-view-lite'

    const payload = {
        repo: 'https://github.com/humanspeak/svelte-json-view-lite',
        email: 'hello@humanspeak.com'
    }

    const isUrl = (s: string) => /^https?:\/\//.test(s)
</script>

<JsonView data={payload}>
    {#snippet string({ value })}
        {#if isUrl(value)}
            <a href={value} target="_blank" rel="noopener" class="text-brand-500 underline">
                {value}
            </a>
        {:else}
            "{value}"
        {/if}
    {/snippet}
</JsonView>

Relative time for Date values

<script lang="ts">
    import { JsonView } from '@humanspeak/svelte-json-view-lite'

    const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })
    const relative = (d: Date) => {
        const diff = d.getTime() - Date.now()
        const days = Math.round(diff / 86400000)
        return rtf.format(days, 'day')
    }
</script>

<JsonView data={{ lastSeen: new Date(Date.now() - 3 * 86400000) }}>
    {#snippet date({ value })}
        <time datetime={value.toISOString()} title={value.toISOString()}>
            {relative(value)}
        </time>
    {/snippet}
</JsonView>
<script lang="ts">
    import { JsonView } from '@humanspeak/svelte-json-view-lite'

    const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })
    const relative = (d: Date) => {
        const diff = d.getTime() - Date.now()
        const days = Math.round(diff / 86400000)
        return rtf.format(days, 'day')
    }
</script>

<JsonView data={{ lastSeen: new Date(Date.now() - 3 * 86400000) }}>
    {#snippet date({ value })}
        <time datetime={value.toISOString()} title={value.toISOString()}>
            {relative(value)}
        </time>
    {/snippet}
</JsonView>

Format large numbers

<script lang="ts">
    import { JsonView } from '@humanspeak/svelte-json-view-lite'

    const fmt = new Intl.NumberFormat('en-US')
</script>

<JsonView data={{ views: 12_345_678, errors: 0 }}>
    {#snippet number({ value })}
        <span class="tabular-nums">{fmt.format(value)}</span>
    {/snippet}
</JsonView>
<script lang="ts">
    import { JsonView } from '@humanspeak/svelte-json-view-lite'

    const fmt = new Intl.NumberFormat('en-US')
</script>

<JsonView data={{ views: 12_345_678, errors: 0 }}>
    {#snippet number({ value })}
        <span class="tabular-nums">{fmt.format(value)}</span>
    {/snippet}
</JsonView>

Badge booleans

<JsonView data={{ active: true, verified: false }}>
    {#snippet boolean({ value })}
        <span
            class="rounded px-1.5 py-0.5 text-xs font-medium"
            class:bg-green-100={value}
            class:text-green-900={value}
            class:bg-red-100={!value}
            class:text-red-900={!value}
        >
            {value ? 'yes' : 'no'}
        </span>
    {/snippet}
</JsonView>
<JsonView data={{ active: true, verified: false }}>
    {#snippet boolean({ value })}
        <span
            class="rounded px-1.5 py-0.5 text-xs font-medium"
            class:bg-green-100={value}
            class:text-green-900={value}
            class:bg-red-100={!value}
            class:text-red-900={!value}
        >
            {value ? 'yes' : 'no'}
        </span>
    {/snippet}
</JsonView>

Field-scoped rendering

The field prop lets you key behavior off the parent label:

<JsonView data={{ email: 'ada@example.com', bio: 'Mathematician.' }}>
    {#snippet string({ value, field })}
        {#if field === 'email'}
            <a href={`mailto:${value}`}>{value}</a>
        {:else}
            "{value}"
        {/if}
    {/snippet}
</JsonView>
<JsonView data={{ email: 'ada@example.com', bio: 'Mathematician.' }}>
    {#snippet string({ value, field })}
        {#if field === 'email'}
            <a href={`mailto:${value}`}>{value}</a>
        {:else}
            "{value}"
        {/if}
    {/snippet}
</JsonView>

field is undefined at the root and inside array elements. level is the depth — useful for nested-specific styling.

Custom label renderer

The label snippet replaces the default key rendering. It receives { field, level } and no value:

<JsonView data={payload}>
    {#snippet label({ field, level })}
        <span class="font-mono text-muted-foreground" class:text-brand-500={level === 0}>
            {field}
        </span>
    {/snippet}
</JsonView>
<JsonView data={payload}>
    {#snippet label({ field, level })}
        <span class="font-mono text-muted-foreground" class:text-brand-500={level === 0}>
            {field}
        </span>
    {/snippet}
</JsonView>

Mixing themes and snippets

Snippets only replace the value cell. The surrounding container, punctuation, and field label continue to use StyleProps classes and --sjv-* tokens. Combine both for full theming:

<JsonView data={payload} style={darkStyles}>
    {#snippet string({ value })}
        <em>{value}</em>
    {/snippet}
</JsonView>
<JsonView data={payload} style={darkStyles}>
    {#snippet string({ value })}
        <em>{value}</em>
    {/snippet}
</JsonView>

Related