Skip to content
Edytor
Esc
↑↓navigate↵open⌘Jpreview
On this page

Properties

Notion-style page properties under the title, typed by a schema, stored in the document's data, which users may add or hide.

properties is the page’s schema: one row per property under the title, each with its name and a cell of its type. The values live in the document’s data (data.properties, by id), so they sync and undo like the rest of the page.

<script lang="ts">
	import {
		StarterEditor,
		type StarterProperty
	} from '$lib/components/edytor/starter/index.js';

	const people = [
		{ id: 'ada', label: 'Ada Lovelace' },
		{ id: 'alan', label: 'Alan Turing' }
	];
	const properties: StarterProperty[] = [
		{
			id: 'status',
			name: 'Status',
			type: 'select',
			options: [
				{ value: 'todo', label: 'To do', color: 'gray' },
				{ value: 'doing', label: 'Doing', color: 'blue' },
				{ value: 'done', label: 'Done', color: 'green' }
			]
		},
		{ id: 'owner', name: 'Owner', type: 'person' },
		{ id: 'due', name: 'Due', type: 'date' },
		{ id: 'estimate', name: 'Estimate', type: 'number', hidden: true }
	];
</script>

<StarterEditor {properties} {people} />

Types

type Stored as The cell
text a string an Input
number a number a number Input
select one option’s value a Select of the options, the chosen one a badge in its colour
multi-select a list of option values a badge per value with its remove button, and a Combobox (a Popover over a searchable Command) whose rows toggle a value
date ISO YYYY-MM-DD the Date Picker (a Popover over a Calendar), the date shown in locale (Oct 13, 2026)
checkbox true (unchecked: absent) a Checkbox
url a string a URL Input, and a button to open it
person a list of person ids a badge per person from people, and the same Combobox over them

An empty value ('', an empty list, an unchecked box) removes the property’s key, so data.properties holds only what is set.

Dates

A date is stored as an ISO date, 2026-10-13, whatever the user’s language, so it sorts, compares and syncs the same everywhere. The page shows it in the locale prop (default en-US), medium style, through Intl.DateTimeFormat in UTC: Oct 13, 2026, 13 oct. 2026 in fr-FR. The locale and the time zone are given, never the runtime’s, so the server’s text and the browser’s are the same and the page hydrates without a mismatch; pass your user’s locale from the server (locale={data.locale}) to show their format. The Date Picker’s calendar uses the same locale; Clear removes the date.

A property

Field Default What it does
id Its key in data.properties.
type One of the types above.
name id What the page shows; each cell is labelled by it.
icon none A short icon before the name.
options [] A select or multi-select property’s options: { value, label?, color? }, color a block colour name (gray, brown, orange, yellow, green, blue, purple, pink, red).
hidden false Shown only once a user adds it.
removable true Users may hide it.
placeholder Empty The empty cell’s text.

Add and hide

With editableProperties (the default), each shown property has a hide button, and Add a property is a searchable list of the hidden ones. Hiding a property keeps its value: the hidden properties sit under a collapsible below the shown ones (2 hidden properties), each with its value and a show button, so nothing is lost and a user can bring one back. A user’s choice is the document’s (data.shownProperties, true for a shown one, false for a hidden one), the values stay in data.properties. editableProperties={false} shows the schema as it is, without the hide, show and add controls. A readonly view shows the values only, the hidden ones under the same collapsible.

Replace a cell

propertyCells replaces the cell of every property of a type; the snippet gets the property, its value, a set (one undo step), readonly, people, the locale and the labelId its field is labelled by:

<script lang="ts">
	import {
		StarterEditor,
		type StarterPropertyCellPayload
	} from '$lib/components/edytor/starter/index.js';
</script>

{#snippet stars({ value, set, readonly, labelId }: StarterPropertyCellPayload)}
	<input
		type="range"
		min="0"
		max="5"
		value={Number(value ?? 0)}
		disabled={readonly}
		aria-labelledby={labelId}
		oninput={(event) => set(Number(event.currentTarget.value))}
	/>
{/snippet}

<StarterEditor
	properties={[{ id: 'rating', name: 'Rating', type: 'number' }]}
	propertyCells={{ number: stars }}
/>

propertyList replaces the whole block: it gets the shown properties, the hidden ones, the ones a user may add, the values, set, show, hide, readonly, editable, labelId and cell, the snippet of a property’s cell (yours where propertyCells has one).

Was this page helpful?