or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-element.mdcss-styling.mddecorators.mddirectives.mdindex.mdpolyfill-support.mdproperty-system.mdtemplate-system.md
tile.json

property-system.mddocs/

0

# Property System

1

2

The property system provides reactive property management with decorators, type conversion, and change detection for building reactive custom elements. Properties automatically trigger re-renders when changed and can be synchronized with attributes.

3

4

## Capabilities

5

6

### Property Decorator

7

8

Declares a reactive property that triggers updates when changed.

9

10

```typescript { .api }

11

/**

12

* Declares a reactive property that triggers updates when changed

13

* @param options Configuration options for the property

14

* @returns Property decorator function

15

*/

16

function property(options?: PropertyDeclaration): PropertyDecorator;

17

18

interface PropertyDeclaration {

19

/**

20

* Type hint for attribute conversion (String, Number, Boolean, Array, Object)

21

*/

22

type?: TypeHint;

23

24

/**

25

* Attribute name to observe, or false to disable attribute observation

26

*/

27

attribute?: boolean | string;

28

29

/**

30

* Whether to reflect property value back to attribute

31

*/

32

reflect?: boolean;

33

34

/**

35

* Custom converter for attribute/property conversion

36

*/

37

converter?: AttributeConverter;

38

39

/**

40

* Whether to skip creating a property accessor

41

*/

42

noAccessor?: boolean;

43

44

/**

45

* Function to determine if property has changed

46

*/

47

hasChanged?: HasChanged;

48

}

49

```

50

51

**Usage Examples:**

52

53

```typescript

54

import { LitElement, html, property } from "lit-element";

55

56

class MyElement extends LitElement {

57

@property({ type: String })

58

name = "World";

59

60

@property({ type: Number, reflect: true })

61

count = 0;

62

63

@property({ type: Boolean, attribute: "is-active" })

64

isActive = false;

65

66

@property({ type: Array })

67

items: string[] = [];

68

69

@property({

70

type: String,

71

hasChanged: (newVal, oldVal) => newVal?.toLowerCase() !== oldVal?.toLowerCase()

72

})

73

caseSensitive = "";

74

75

render() {

76

return html`

77

<div>

78

<p>Name: ${this.name}</p>

79

<p>Count: ${this.count}</p>

80

<p>Active: ${this.isActive}</p>

81

<p>Items: ${this.items.join(", ")}</p>

82

</div>

83

`;

84

}

85

}

86

```

87

88

### State Decorator

89

90

Declares internal reactive state that doesn't reflect to attributes.

91

92

```typescript { .api }

93

/**

94

* Declares internal reactive state that doesn't reflect to attributes

95

* @param options Configuration options for the state property

96

* @returns Property decorator function

97

*/

98

function state(options?: StateDeclaration): PropertyDecorator;

99

100

interface StateDeclaration {

101

/**

102

* Function to determine if state has changed

103

*/

104

hasChanged?: HasChanged;

105

}

106

```

107

108

**Usage Examples:**

109

110

```typescript

111

import { LitElement, html, property, state } from "lit-element";

112

113

class MyElement extends LitElement {

114

@property({ type: String })

115

title = "";

116

117

@state()

118

private _expanded = false;

119

120

@state()

121

private _loading = false;

122

123

private _toggle() {

124

this._expanded = !this._expanded;

125

}

126

127

render() {

128

return html`

129

<div>

130

<h2 @click=${this._toggle}>${this.title}</h2>

131

${this._expanded ? html`

132

<div class="content">

133

${this._loading ? html`<p>Loading...</p>` : html`<p>Content here</p>`}

134

</div>

135

` : ''}

136

</div>

137

`;

138

}

139

}

140

```

141

142

### Static Properties

143

144

Alternative to decorators for JavaScript or when decorators aren't preferred.

145

146

```typescript { .api }

147

/**

148

* Static property declarations for reactive properties

149

*/

150

static properties: PropertyDeclarations;

151

152

interface PropertyDeclarations {

153

[key: string]: PropertyDeclaration;

154

}

155

```

156

157

**Usage Examples:**

158

159

```typescript

160

import { LitElement, html } from "lit-element";

161

162

class MyElement extends LitElement {

163

static properties = {

164

name: { type: String },

165

count: { type: Number, reflect: true },

166

items: { type: Array },

167

_internal: { state: true }

168

};

169

170

constructor() {

171

super();

172

this.name = "World";

173

this.count = 0;

174

this.items = [];

175

this._internal = false;

176

}

177

178

render() {

179

return html`<p>Hello, ${this.name}!</p>`;

180

}

181

}

182

```

183

184

### Type Hints

185

186

Type hints for automatic attribute conversion.

187

188

```typescript { .api }

189

/**

190

* Type hint for property conversion

191

*/

192

type TypeHint =

193

| typeof String // Converts to/from string attributes

194

| typeof Number // Converts to/from numeric attributes

195

| typeof Boolean // Converts to/from boolean attributes (presence-based)

196

| typeof Array // JSON serialization for complex types

197

| typeof Object; // JSON serialization for complex types

198

```

199

200

### Attribute Converter

201

202

Custom conversion between attributes and properties.

203

204

```typescript { .api }

205

/**

206

* Interface for custom attribute/property conversion

207

*/

208

interface AttributeConverter<Type = unknown, TypeHint = unknown> {

209

/**

210

* Convert attribute value to property value

211

* @param value Attribute value (string or null)

212

* @param type Type hint for conversion

213

* @returns Converted property value

214

*/

215

fromAttribute?(value: string | null, type?: TypeHint): Type;

216

217

/**

218

* Convert property value to attribute value

219

* @param value Property value

220

* @param type Type hint for conversion

221

* @returns Converted attribute value (string, number, boolean, or null)

222

*/

223

toAttribute?(value: Type, type?: TypeHint): unknown;

224

}

225

```

226

227

**Usage Examples:**

228

229

```typescript

230

import { LitElement, html, property } from "lit-element";

231

232

const dateConverter = {

233

fromAttribute: (value: string | null) => {

234

return value ? new Date(value) : null;

235

},

236

toAttribute: (value: Date | null) => {

237

return value ? value.toISOString() : null;

238

}

239

};

240

241

class MyElement extends LitElement {

242

@property({ converter: dateConverter })

243

createdAt: Date | null = null;

244

245

render() {

246

return html`

247

<p>Created: ${this.createdAt?.toLocaleDateString() || 'Not set'}</p>

248

`;

249

}

250

}

251

```

252

253

### Has Changed Function

254

255

Function to determine if a property value has changed.

256

257

```typescript { .api }

258

/**

259

* Function to determine if property has changed

260

* @param value New property value

261

* @param oldValue Previous property value

262

* @returns True if property has changed

263

*/

264

interface HasChanged {

265

(value: unknown, oldValue: unknown): boolean;

266

}

267

268

/**

269

* Default hasChanged implementation using strict inequality

270

*/

271

function notEqual(value: unknown, oldValue: unknown): boolean;

272

```

273

274

### Default Converter

275

276

Default converter used for standard type conversion.

277

278

```typescript { .api }

279

/**

280

* Default attribute converter with support for standard types

281

*/

282

const defaultConverter: AttributeConverter;

283

```

284

285

**Usage Examples:**

286

287

```typescript

288

import { LitElement, html, property, notEqual } from "lit-element";

289

290

class MyElement extends LitElement {

291

@property({

292

type: String,

293

hasChanged: (newVal, oldVal) => {

294

// Case-insensitive comparison

295

return newVal?.toLowerCase() !== oldVal?.toLowerCase();

296

}

297

})

298

name = "";

299

300

@property({

301

type: Number,

302

hasChanged: (newVal, oldVal) => {

303

// Only update if difference is significant

304

return Math.abs(Number(newVal) - Number(oldVal)) > 0.1;

305

}

306

})

307

value = 0;

308

309

render() {

310

return html`

311

<div>

312

<p>Name: ${this.name}</p>

313

<p>Value: ${this.value}</p>

314

</div>

315

`;

316

}

317

}

318

```

319

320

### Property Change Detection

321

322

Understanding how property changes are detected and handled.

323

324

```typescript { .api }

325

/**

326

* Request an update which will trigger the update lifecycle

327

* @param name Property name that changed

328

* @param oldValue Previous value of the property

329

* @param options Additional options for the update request

330

*/

331

requestUpdate(name?: PropertyKey, oldValue?: unknown, options?: RequestUpdateOptions): void;

332

333

interface RequestUpdateOptions {

334

/**

335

* Whether to trigger an update even if hasChanged returns false

336

*/

337

force?: boolean;

338

}

339

```

340

341

**Usage Examples:**

342

343

```typescript

344

import { LitElement, html } from "lit-element";

345

346

class MyElement extends LitElement {

347

private _data: any[] = [];

348

349

get data() {

350

return this._data;

351

}

352

353

set data(value: any[]) {

354

const oldValue = this._data;

355

this._data = value;

356

// Manually request update for array property

357

this.requestUpdate('data', oldValue);

358

}

359

360

addItem(item: any) {

361

this._data.push(item);

362

// Request update after mutating array

363

this.requestUpdate('data', this._data);

364

}

365

366

render() {

367

return html`

368

<ul>

369

${this.data.map(item => html`<li>${item}</li>`)}

370

</ul>

371

`;

372

}

373

}

374

```