Skip to content

Custom elements

LiveStore can be used with custom elements/web components.

See examples for a complete example.

import {
const makePersistedAdapter: (options: WebAdapterOptions) => Adapter

Creates a web adapter with persistent storage (currently only supports OPFS). Requires both a web worker and a shared worker.

On browsers without SharedWorker support (e.g. Android Chrome), this adapter automatically falls back to single-tab mode. In single-tab mode:

  • Each tab runs independently with its own leader worker
  • Multi-tab synchronization is not available
  • Devtools are not supported

@seehttps://github.com/livestorejs/livestore/issues/321 - SharedWorker tracking issue

@seehttps://issues.chromium.org/issues/40290702 - Chromium SharedWorker bug

@example

import { makePersistedAdapter } from '@livestore/adapter-web'
import LiveStoreWorker from './livestore.worker.ts?worker'
import LiveStoreSharedWorker from '@livestore/adapter-web/shared-worker?sharedworker'
const adapter = makePersistedAdapter({
worker: LiveStoreWorker,
sharedWorker: LiveStoreSharedWorker,
storage: { type: 'opfs' },
})

makePersistedAdapter
} from '@livestore/adapter-web'
import
const LiveStoreSharedWorker: new (options?: {
name?: string;
}) => SharedWorker
LiveStoreSharedWorker
from '@livestore/adapter-web/shared-worker?sharedworker'
import {
const createStorePromise: <TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}, TSyncPayloadSchema extends Codec<Json, Json> = Codec<Json, Json, never, never>>({ signal, otelOptions, ...options }: CreateStoreOptionsPromise<TSchema, TContext, TSyncPayloadSchema>) => Promise<Store<TSchema, TContext>>

Create a new LiveStore Store

createStorePromise
,
const queryDb: {
<TResultSchema, TResult = TResultSchema>(queryInput: QueryInputRaw<TResultSchema, ReadonlyArray<any>> | QueryBuilder<TResultSchema, any, any>, options?: {
map?: (rows: TResultSchema) => TResult;
label?: string;
deps?: DepKey;
}): LiveQueryDef<TResult>;
<TResultSchema, TResult = TResultSchema>(queryInput: ((get: GetAtomResult) => QueryInputRaw<TResultSchema, ReadonlyArray<any>>) | ((get: GetAtomResult) => QueryBuilder<TResultSchema, any, any>), options?: {
map?: (rows: TResultSchema) => TResult;
label?: string;
deps?: DepKey;
}): LiveQueryDef<TResult>;
}

NOTE queryDb is only supposed to read data. Don't use it to insert/update/delete data but use events instead.

When using contextual data when constructing the query, please make sure to include it in the deps option.

@example

const todos$ = queryDb(tables.todos.where({ complete: true }))

@example

// Group-by raw SQL query
const colorCounts$ = queryDb({
query: sql`SELECT color, COUNT(*) as count FROM todos WHERE complete = ? GROUP BY color`,
schema: Schema.Array(Schema.Struct({
color: Schema.String,
count: Schema.Number,
})),
bindValues: [1],
})

@example

// Using contextual data when constructing the query
const makeFilteredQuery = (filter: string) =>
queryDb(tables.todos.where({ title: { op: 'like', value: filter } }), { deps: [filter] })
const filteredTodos$ = makeFilteredQuery('buy coffee')

queryDb
} from '@livestore/livestore'
import
const LiveStoreWorker: new (options?: {
name?: string;
}) => Worker
LiveStoreWorker
from './livestore/livestore.worker.ts?worker'
import {
import events
events
,
import schema
schema
,
import tables
tables
} from './livestore/schema.ts'
const
const adapter: Adapter
adapter
=
function makePersistedAdapter(options: WebAdapterOptions): Adapter

Creates a web adapter with persistent storage (currently only supports OPFS). Requires both a web worker and a shared worker.

On browsers without SharedWorker support (e.g. Android Chrome), this adapter automatically falls back to single-tab mode. In single-tab mode:

  • Each tab runs independently with its own leader worker
  • Multi-tab synchronization is not available
  • Devtools are not supported

@seehttps://github.com/livestorejs/livestore/issues/321 - SharedWorker tracking issue

@seehttps://issues.chromium.org/issues/40290702 - Chromium SharedWorker bug

@example

import { makePersistedAdapter } from '@livestore/adapter-web'
import LiveStoreWorker from './livestore.worker.ts?worker'
import LiveStoreSharedWorker from '@livestore/adapter-web/shared-worker?sharedworker'
const adapter = makePersistedAdapter({
worker: LiveStoreWorker,
sharedWorker: LiveStoreSharedWorker,
storage: { type: 'opfs' },
})

makePersistedAdapter
({
storage: {
readonly type: "opfs";
readonly directory?: string | undefined;
}

Specifies where to persist data for this adapter

storage
: {
type: "opfs"
type
: 'opfs' },
worker: ((options: {
name: string;
}) => globalThis.Worker) | (new (options: {
name: string;
}) => globalThis.Worker)
worker
:
const LiveStoreWorker: new (options?: {
name?: string;
}) => Worker
LiveStoreWorker
,
sharedWorker: ((options: {
name: string;
}) => globalThis.SharedWorker) | (new (options: {
name: string;
}) => globalThis.SharedWorker)

This is mostly an implementation detail and needed to be exposed into app code due to a current Vite limitation (https://github.com/vitejs/vite/issues/8427).

In most cases this should look like:

import LiveStoreSharedWorker from '@livestore/adapter-web/shared-worker?sharedworker'
const adapter = makePersistedAdapter({
sharedWorker: LiveStoreSharedWorker,
// ...
})

sharedWorker
:
const LiveStoreSharedWorker: new (options?: {
name?: string;
}) => SharedWorker
LiveStoreSharedWorker
,
})
const
const store: Store<any, {}>
store
= await
createStorePromise<any, {}, Codec<Json, Json, never, never>>({ signal, otelOptions, ...options }: CreateStoreOptionsPromise<any, {}, Codec<Json, Json, never, never>>): Promise<Store<any, {}>>

Create a new LiveStore Store

createStorePromise
({
CreateStoreOptions<any, {}, Codec<Json, Json, never, never>>.schema: any

The LiveStore schema defining tables, events, and materializers.

schema
,
CreateStoreOptions<TSchema extends LiveStoreSchema, TContext = {}, TSyncPayloadSchema extends Codec<Json, Json> = Codec<Json, Json, never, never>>.adapter: Adapter

Adapter used for data storage and synchronization.

adapter
,
CreateStoreOptions<TSchema extends LiveStoreSchema, TContext = {}, TSyncPayloadSchema extends Codec<Json, Json> = Codec<Json, Json, never, never>>.storeId: string

Unique identifier for the Store instance, stable for its lifetime.

  • Valid characters: Only alphanumeric characters, underscores (_), and hyphens (-) are allowed. Must match /^[a-zA-Z0-9_-]+$/.
  • Globally unique: Use globally unique IDs (e.g., nanoid) to prevent collisions across stores.
  • Use namespaces: Prefix to avoid collisions and for easier identification when debugging (e.g., app-root, workspace-abc123, issue-456)

storeId
: 'custom-elements-demo' })
const
const visibleTodos$: LiveQueryDef<unknown, "def">
visibleTodos$
=
queryDb<unknown, unknown>(queryInput: QueryInputRaw<unknown, readonly any[]> | QueryBuilder<unknown, any, any>, options?: {
map?: (rows: unknown) => unknown;
label?: string;
deps?: DepKey;
} | undefined): LiveQueryDef<unknown, "def"> (+1 overload)

NOTE queryDb is only supposed to read data. Don't use it to insert/update/delete data but use events instead.

When using contextual data when constructing the query, please make sure to include it in the deps option.

@example

const todos$ = queryDb(tables.todos.where({ complete: true }))

@example

// Group-by raw SQL query
const colorCounts$ = queryDb({
query: sql`SELECT color, COUNT(*) as count FROM todos WHERE complete = ? GROUP BY color`,
schema: Schema.Array(Schema.Struct({
color: Schema.String,
count: Schema.Number,
})),
bindValues: [1],
})

@example

// Using contextual data when constructing the query
const makeFilteredQuery = (filter: string) =>
queryDb(tables.todos.where({ title: { op: 'like', value: filter } }), { deps: [filter] })
const filteredTodos$ = makeFilteredQuery('buy coffee')

queryDb
(
import tables
tables
.
any
todos
.
any
where
({
deletedAt: null
deletedAt
: null }))
class
class TodoListElement
TodoListElement
extends
var HTMLElement: {
new (): HTMLElement;
prototype: HTMLElement;
}

The HTMLElement interface represents any HTML element. Some elements directly implement this interface, while others implement it via an interface that inherits it.

MDN Reference

HTMLElement
{
private
TodoListElement.list: HTMLUListElement
list
:
interface HTMLUListElement

The HTMLUListElement interface provides special properties (beyond those defined on the regular HTMLElement interface it also has available to it by inheritance) for manipulating unordered list () elements.

MDN Reference

HTMLUListElement
private
TodoListElement.input: HTMLInputElement
input
:
interface HTMLInputElement

The HTMLInputElement interface provides special properties and methods for manipulating the options, layout, and presentation of elements.

MDN Reference

HTMLInputElement
constructor() {
super()
const
const shadow: ShadowRoot
shadow
= this.
Element.attachShadow(init: ShadowRootInit): ShadowRoot

The Element.attachShadow() method attaches a shadow DOM tree to the specified element and returns a reference to its ShadowRoot.

MDN Reference

attachShadow
({
ShadowRootInit.mode: ShadowRootMode
mode
: 'open' })
this.
TodoListElement.input: HTMLInputElement
input
=
var document: Document

window.document returns a reference to the document contained in the window.

MDN Reference

document
.
Document.createElement<"input">(tagName: "input", options?: ElementCreationOptions): HTMLInputElement (+2 overloads)

In an HTML document, the document.createElement() method creates the HTML element specified by localName, or an HTMLUnknownElement if localName isn't recognized.

MDN Reference

createElement
('input')
this.
TodoListElement.input: HTMLInputElement
input
.
HTMLInputElement.placeholder: string

The placeholder property of the HTMLInputElement interface represents a hint to the user of what can be entered in the control. It reflects the element's placeholder attribute.

MDN Reference

placeholder
= 'What needs to be done?'
this.
TodoListElement.list: HTMLUListElement
list
=
var document: Document

window.document returns a reference to the document contained in the window.

MDN Reference

document
.
Document.createElement<"ul">(tagName: "ul", options?: ElementCreationOptions): HTMLUListElement (+2 overloads)

In an HTML document, the document.createElement() method creates the HTML element specified by localName, or an HTMLUnknownElement if localName isn't recognized.

MDN Reference

createElement
('ul')
this.
TodoListElement.list: HTMLUListElement
list
.
ElementCSSInlineStyle.style: CSSStyleDeclaration
style
.
CSSStyleProperties.listStyle: string

The list-style CSS shorthand property allows you to set all the list style properties at once.

MDN Reference

listStyle
= 'none'
this.
TodoListElement.list: HTMLUListElement
list
.
ElementCSSInlineStyle.style: CSSStyleDeclaration
style
.
CSSStyleProperties.padding: string

The padding CSS shorthand property sets the padding area on all four sides of an element at once.

MDN Reference

padding
= '0'
this.
TodoListElement.list: HTMLUListElement
list
.
ElementCSSInlineStyle.style: CSSStyleDeclaration
style
.
CSSStyleProperties.margin: string

The margin CSS shorthand property sets the margin area on all four sides of an element.

MDN Reference

margin
= '16px 0 0'
this.
TodoListElement.input: HTMLInputElement
input
.
HTMLInputElement.addEventListener<"keydown">(type: "keydown", listener: (this: HTMLInputElement, ev: KeyboardEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)

The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.

MDN Reference

Adds a new handler for the type event. Any given listener is added only once per type and per capture option value.

If the once option is true, the listener is removed after the next time a type event is dispatched.

The capture option is not used by Node.js in any functional way other than tracking registered event listeners per the EventTarget specification. Specifically, the capture option is used as part of the key when registering a listener. Any individual listener may be added once with capture = false, and once with capture = true.

The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.

MDN Reference

Adds a new handler for the type event. Any given listener is added only once per type and per capture option value.

If the once option is true, the listener is removed after the next time a type event is dispatched.

The capture option is not used by Node.js in any functional way other than tracking registered event listeners per the EventTarget specification. Specifically, the capture option is used as part of the key when registering a listener. Any individual listener may be added once with capture = false, and once with capture = true.

addEventListener
('keydown', (
event: KeyboardEvent
event
) => {
if (
event: KeyboardEvent
event
.
KeyboardEvent.key: string

The KeyboardEvent interface's key read-only property returns the value of the key pressed by the user, taking into consideration the state of modifier keys such as Shift as well as the keyboard locale and layout.

MDN Reference

key
=== 'Enter' && this.
TodoListElement.input: HTMLInputElement
input
.
HTMLInputElement.value: string

The value property of the HTMLInputElement interface represents the current value of the element as a string.

MDN Reference

value
.
String.trim(): string

Removes the leading and trailing white space and line terminator characters from a string.

trim
() !== '') {
const store: Store<any, {}>
store
.
Store<any, {}>.commit: <readonly [any]>(list_0: any) => void (+3 overloads)
commit
(
import events
events
.
any
todoCreated
({
id: `${string}-${string}-${string}-${string}-${string}`
id
:
var crypto: Crypto
crypto
.
Crypto.randomUUID(): `${string}-${string}-${string}-${string}-${string}` (+2 overloads)
randomUUID
(),
text: string
text
: this.
TodoListElement.input: HTMLInputElement
input
.
HTMLInputElement.value: string

The value property of the HTMLInputElement interface represents the current value of the element as a string.

MDN Reference

value
.
String.trim(): string

Removes the leading and trailing white space and line terminator characters from a string.

trim
() }))
this.
TodoListElement.input: HTMLInputElement
input
.
HTMLInputElement.value: string

The value property of the HTMLInputElement interface represents the current value of the element as a string.

MDN Reference

value
= ''
}
})
const shadow: ShadowRoot
shadow
.
ParentNode.append(...nodes: (Node | string)[]): void

Inserts nodes after the last child of node, while replacing strings in nodes with equivalent Text nodes.

Throws a "HierarchyRequestError" DOMException if the constraints of the node tree are violated.

MDN Reference

append
(this.
TodoListElement.input: HTMLInputElement
input
, this.
TodoListElement.list: HTMLUListElement
list
)
}
TodoListElement.connectedCallback(): void
connectedCallback
(): void {
this.
TodoListElement.renderTodos(todos: ReadonlyArray<typeof tables.todos.Type>): void
renderTodos
(
var Array: ArrayConstructor
Array
.
ArrayConstructor.from<any>(iterable: Iterable<any> | ArrayLike<any>): any[] (+3 overloads)

Creates an array from an iterable object.

@paramiterable An iterable object to convert to an array.

from
(
const store: Store<any, {}>
store
.
Store<any, {}>.query: <Iterable<any> | ArrayLike<any>>(query: Queryable<Iterable<any> | ArrayLike<any>> | {
query: string;
bindValues: Bindable;
schema?: Decoder<Iterable<any> | ArrayLike<any>, never>;
}, options?: {
otelContext?: Context;
debugRefreshReason?: RefreshReason;
}) => Iterable<any> | ArrayLike<any>

Synchronously queries the database without creating a LiveQuery. This is useful for queries that don't need to be reactive.

Example: Query builder

const completedTodos = store.query(tables.todo.where({ complete: true }))

Example: Raw SQL query

const completedTodos = store.query({ query: 'SELECT * FROM todo WHERE complete = 1', bindValues: {} })

query
(
import tables
tables
.
any
todos
.
any
where
({
deletedAt: null
deletedAt
: null }))))
const store: Store<any, {}>
store
.
Store<any, {}>.subscribe: <unknown>(query: Queryable<unknown>, onUpdate: (value: unknown) => void, options?: SubscribeOptions<unknown> | undefined) => Unsubscribe (+1 overload)
subscribe
(
const visibleTodos$: LiveQueryDef<unknown, "def">
visibleTodos$
, (
todos: unknown
todos
) => this.
TodoListElement.renderTodos(todos: ReadonlyArray<typeof tables.todos.Type>): void
renderTodos
(
todos: unknown
todos
))
}
private
TodoListElement.renderTodos(todos: ReadonlyArray<typeof tables.todos.Type>): void
renderTodos
(
todos: readonly any[]
todos
:
interface ReadonlyArray<T>
ReadonlyArray
<typeof
import tables
tables
.
any
todos
.
any
Type
>): void {
const
const nodes: HTMLLIElement[]
nodes
=
var Array: ArrayConstructor
Array
.
ArrayConstructor.from<any, HTMLLIElement>(iterable: Iterable<any> | ArrayLike<any>, mapfn: (v: any, k: number) => HTMLLIElement, thisArg?: any): HTMLLIElement[] (+3 overloads)

Creates an array from an iterable object.

@paramiterable An iterable object to convert to an array.

@parammapfn A mapping function to call on every element of the array.

@paramthisArg Value of 'this' used to invoke the mapfn.

from
(
todos: readonly any[]
todos
, (
todo: any
todo
) => {
const
const item: HTMLLIElement
item
=
var document: Document

window.document returns a reference to the document contained in the window.

MDN Reference

document
.
Document.createElement<"li">(tagName: "li", options?: ElementCreationOptions): HTMLLIElement (+2 overloads)

In an HTML document, the document.createElement() method creates the HTML element specified by localName, or an HTMLUnknownElement if localName isn't recognized.

MDN Reference

createElement
('li')
const item: HTMLLIElement
item
.
Element.textContent: string | null
textContent
=
todo: any
todo
.
any
text
const item: HTMLLIElement
item
.
ElementCSSInlineStyle.style: CSSStyleDeclaration
style
.
CSSStyleProperties.cursor: string

The cursor CSS property sets the mouse cursor, if any, to show when the mouse pointer is over an element.

MDN Reference

cursor
= 'pointer'
const item: HTMLLIElement
item
.
HTMLLIElement.addEventListener<"click">(type: "click", listener: (this: HTMLLIElement, ev: PointerEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)

The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.

MDN Reference

Adds a new handler for the type event. Any given listener is added only once per type and per capture option value.

If the once option is true, the listener is removed after the next time a type event is dispatched.

The capture option is not used by Node.js in any functional way other than tracking registered event listeners per the EventTarget specification. Specifically, the capture option is used as part of the key when registering a listener. Any individual listener may be added once with capture = false, and once with capture = true.

The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.

MDN Reference

Adds a new handler for the type event. Any given listener is added only once per type and per capture option value.

If the once option is true, the listener is removed after the next time a type event is dispatched.

The capture option is not used by Node.js in any functional way other than tracking registered event listeners per the EventTarget specification. Specifically, the capture option is used as part of the key when registering a listener. Any individual listener may be added once with capture = false, and once with capture = true.

addEventListener
('click', () => {
const store: Store<any, {}>
store
.
Store<any, {}>.commit: <readonly [any]>(list_0: any) => void (+3 overloads)
commit
(
todo: any
todo
.
any
completed
=== true ?
import events
events
.
any
todoUncompleted
({
id: any
id
:
todo: any
todo
.
any
id
}) :
import events
events
.
any
todoCompleted
({
id: any
id
:
todo: any
todo
.
any
id
}),
)
})
const
const deleteButton: HTMLButtonElement
deleteButton
=
var document: Document

window.document returns a reference to the document contained in the window.

MDN Reference

document
.
Document.createElement<"button">(tagName: "button", options?: ElementCreationOptions): HTMLButtonElement (+2 overloads)

In an HTML document, the document.createElement() method creates the HTML element specified by localName, or an HTMLUnknownElement if localName isn't recognized.

MDN Reference

createElement
('button')
const deleteButton: HTMLButtonElement
deleteButton
.
HTMLButtonElement.type: "button" | "reset" | "submit"

The type property of the HTMLButtonElement interface is a string that indicates the behavior type of the element.

MDN Reference

type
= 'button'
const deleteButton: HTMLButtonElement
deleteButton
.
Element.textContent: string | null
textContent
= '✕'
const deleteButton: HTMLButtonElement
deleteButton
.
ElementCSSInlineStyle.style: CSSStyleDeclaration
style
.
CSSStyleProperties.marginLeft: string

The margin-left CSS property sets the margin area on the left side of an element. A positive value places it farther from its neighbors, while a negative value places it closer.

MDN Reference

marginLeft
= '8px'
const deleteButton: HTMLButtonElement
deleteButton
.
HTMLButtonElement.addEventListener<"click">(type: "click", listener: (this: HTMLButtonElement, ev: PointerEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)

The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.

MDN Reference

Adds a new handler for the type event. Any given listener is added only once per type and per capture option value.

If the once option is true, the listener is removed after the next time a type event is dispatched.

The capture option is not used by Node.js in any functional way other than tracking registered event listeners per the EventTarget specification. Specifically, the capture option is used as part of the key when registering a listener. Any individual listener may be added once with capture = false, and once with capture = true.

The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.

MDN Reference

Adds a new handler for the type event. Any given listener is added only once per type and per capture option value.

If the once option is true, the listener is removed after the next time a type event is dispatched.

The capture option is not used by Node.js in any functional way other than tracking registered event listeners per the EventTarget specification. Specifically, the capture option is used as part of the key when registering a listener. Any individual listener may be added once with capture = false, and once with capture = true.

addEventListener
('click', (
event: PointerEvent
event
) => {
event: PointerEvent
event
.
Event.stopPropagation(): void (+2 overloads)

This is not used in Node.js and is provided purely for completeness.

stopPropagation
()
const store: Store<any, {}>
store
.
Store<any, {}>.commit: <readonly [any]>(list_0: any) => void (+3 overloads)
commit
(
import events
events
.
any
todoDeleted
({
id: any
id
:
todo: any
todo
.
any
id
,
deletedAt: Date
deletedAt
: new
var Date: DateConstructor
new () => Date (+3 overloads)
Date
() }))
})
const
const row: HTMLDivElement
row
=
var document: Document

window.document returns a reference to the document contained in the window.

MDN Reference

document
.
Document.createElement<"div">(tagName: "div", options?: ElementCreationOptions): HTMLDivElement (+2 overloads)

In an HTML document, the document.createElement() method creates the HTML element specified by localName, or an HTMLUnknownElement if localName isn't recognized.

MDN Reference

createElement
('div')
const row: HTMLDivElement
row
.
ElementCSSInlineStyle.style: CSSStyleDeclaration
style
.
CSSStyleProperties.display: string

The display CSS property sets whether an element is treated as a block or inline box and the layout used for its children, such as flow layout, grid or flex.

MDN Reference

display
= 'flex'
const row: HTMLDivElement
row
.
ElementCSSInlineStyle.style: CSSStyleDeclaration
style
.
CSSStyleProperties.alignItems: string

The CSS align-items property sets the align-self value on all direct children as a group. In flexbox, it controls the alignment of items on the cross axis. In grid layout, it controls the alignment of items on the block axis within their grid areas.

MDN Reference

alignItems
= 'center'
const row: HTMLDivElement
row
.
Node.appendChild<HTMLLIElement>(node: HTMLLIElement): HTMLLIElement

The appendChild() method of the Node interface adds a node to the end of the list of children of a specified parent node.

MDN Reference

appendChild
(
const item: HTMLLIElement
item
)
const row: HTMLDivElement
row
.
Node.appendChild<HTMLButtonElement>(node: HTMLButtonElement): HTMLButtonElement

The appendChild() method of the Node interface adds a node to the end of the list of children of a specified parent node.

MDN Reference

appendChild
(
const deleteButton: HTMLButtonElement
deleteButton
)
const
const wrapper: HTMLLIElement
wrapper
=
var document: Document

window.document returns a reference to the document contained in the window.

MDN Reference

document
.
Document.createElement<"li">(tagName: "li", options?: ElementCreationOptions): HTMLLIElement (+2 overloads)

In an HTML document, the document.createElement() method creates the HTML element specified by localName, or an HTMLUnknownElement if localName isn't recognized.

MDN Reference

createElement
('li')
const wrapper: HTMLLIElement
wrapper
.
Node.appendChild<HTMLDivElement>(node: HTMLDivElement): HTMLDivElement

The appendChild() method of the Node interface adds a node to the end of the list of children of a specified parent node.

MDN Reference

appendChild
(
const row: HTMLDivElement
row
)
return
const wrapper: HTMLLIElement
wrapper
})
this.
TodoListElement.list: HTMLUListElement
list
.
ParentNode.replaceChildren(...nodes: (Node | string)[]): void

Replace all children of node with nodes, while replacing strings in nodes with equivalent Text nodes.

Throws a "HierarchyRequestError" DOMException if the constraints of the node tree are violated.

MDN Reference

replaceChildren
(...
const nodes: HTMLLIElement[]
nodes
)
}
}
var customElements: CustomElementRegistry

The customElements read-only property of the Window interface returns a reference to the CustomElementRegistry object, which can be used to register new custom elements and get information about previously registered custom elements.

MDN Reference

customElements
.
CustomElementRegistry.define(name: string, constructor: CustomElementConstructor, options?: ElementDefinitionOptions): void

The define() method of the CustomElementRegistry interface adds a definition for a custom element to the custom element registry, mapping its name to the constructor which will be used to create it.

MDN Reference

define
('todo-list',
class TodoListElement
TodoListElement
)