---
title: Core concepts
description: How an authored default differs from live state, and what happens
  to properties assigned before an element upgrades.
editUrl: true
head: []
template: doc
sidebar:
  hidden: false
  attrs: {}
pagefind: true
draft: false
---

Two contracts are easy to get wrong because neither has a visible symptom until it matters: which
value survives a form reset, and what happens to a property you assign before the element definition
loads. Everything else about a component is on its own page — this covers the two rules that span
all of them.

## Authored defaults and live state

The `value` **attribute** you author is the default. The `value` **property** is live state. They
are separate storage, exactly as they are on a native `<input>`:

```html
<ui-select value="draft" name="status">…</ui-select>
```

```ts
const select = document.querySelector('ui-select')

select.value // 'draft'  — live state, currently equal to the default
select.value = 'published' // live state moves
select.defaultValue // 'draft'  — the authored attribute is untouched
select.getAttribute('value') // 'draft'
```

Two consequences follow.

**A form reset returns to the authored attribute, not to whatever was assigned last.**
Form-associated elements — Select, Combobox, Listbox, and OTP Field — implement `formResetCallback`
by restoring `defaultValue`, so `<button type="reset">` puts `status` back to `draft`.

**Assigning the property dispatches nothing.** `ui-before-change` and `ui-change` describe _user_
transitions and carry the originating event in `detail.originalEvent`. A programmatic assignment has
no originating event, so it updates the DOM silently. Code that assigns a value already knows it
did; if you need your own listeners to run, dispatch your own event.

To move the default itself — so a later reset returns somewhere new — set the attribute:

```ts
select.setAttribute('value', 'published')
```

The helpers the elements use to keep the two apart are exported from
[`@timelessui/components/value-state`](/docs/reference/packages/#entrypoints), if you are building
something on the same contract.

## Properties assigned before upgrade

A framework often has a reference to an element before `customElements.define` has run for it. An
assignment at that moment lands as an own property on the instance, shadowing the accessor the
definition will later install on the prototype. In most custom-element libraries the value is then
lost.

Timeless replays it. On connection, each declared attribute and property that exists as an own
property is deleted and re-assigned, which runs the real setter — and this happens _before_ the
component's own connection logic, so the element never observes a stale value:

```ts
const select = document.createElement('ui-select')
select.value = 'published' // no definition yet — a plain own property

await import('@timelessui/components/register/ui-select')
document.body.append(select) // upgrade, then replay, then connect

select.value // 'published'
```

This is why registration is safe to load late, and why a server-rendered page can hydrate and assign
state without ordering the registration import first. See
[Packages and entrypoints](/docs/reference/packages/#server-rendering) for the import-safety rules
and your framework's guide under [Frameworks](/docs/frameworks/vanilla/) for where the assignment
usually lives.

## Where the rest lives

- **Anatomy, attributes, parts, states, events** — each component's own page, generated from the
  contract the stylesheets are proven against.
- **The two kinds of component, and how each is configured** —
  [AI agents](/docs/getting-started/agents/).
- **Layers, tokens, and overriding styles** — [Theming](/docs/styling/theming/).
- **Import safety and server rendering** — [Packages and entrypoints](/docs/reference/packages/).
- **What the platform handles and what degrades** —
  [Browser support](/docs/reference/browser-support/).