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

directives.mddocs/

0

# Directives

1

2

Built-in directives for advanced templating patterns, conditional rendering, and performance optimization. Directives extend lit-html's templating capabilities with reusable, efficient patterns for common UI scenarios.

3

4

## Capabilities

5

6

### Repeat Directive

7

8

Efficiently renders lists with keyed items for optimal performance.

9

10

```typescript { .api }

11

/**

12

* Renders a list of items with efficient updates using keys

13

* @param items Iterable of items to render

14

* @param keyFn Function to generate unique keys for items

15

* @param template Function to render each item

16

* @returns DirectiveResult for list rendering

17

*/

18

function repeat<T>(

19

items: Iterable<T>,

20

keyFn: KeyFn<T>,

21

template: ItemTemplate<T>

22

): DirectiveResult<typeof RepeatDirective>;

23

24

type KeyFn<T> = (item: T, index: number) => unknown;

25

type ItemTemplate<T> = (item: T, index: number) => unknown;

26

```

27

28

**Usage Examples:**

29

30

```typescript

31

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

32

33

interface User {

34

id: number;

35

name: string;

36

email: string;

37

}

38

39

class UserList extends LitElement {

40

users: User[] = [

41

{ id: 1, name: "Alice", email: "alice@example.com" },

42

{ id: 2, name: "Bob", email: "bob@example.com" },

43

{ id: 3, name: "Charlie", email: "charlie@example.com" }

44

];

45

46

render() {

47

return html`

48

<ul>

49

${repeat(

50

this.users,

51

(user) => user.id, // Key function

52

(user, index) => html` // Template function

53

<li>

54

<strong>${user.name}</strong> (${index + 1})

55

<br />

56

<small>${user.email}</small>

57

</li>

58

`

59

)}

60

</ul>

61

`;

62

}

63

}

64

```

65

66

### Class Map Directive

67

68

Conditionally applies CSS classes based on an object map.

69

70

```typescript { .api }

71

/**

72

* Sets CSS classes on an element based on a map of class names to boolean values

73

* @param classInfo Object mapping class names to boolean conditions

74

* @returns DirectiveResult for conditional class application

75

*/

76

function classMap(classInfo: ClassInfo): DirectiveResult<typeof ClassMapDirective>;

77

78

interface ClassInfo {

79

[name: string]: string | boolean | number;

80

}

81

```

82

83

**Usage Examples:**

84

85

```typescript

86

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

87

88

class ButtonElement extends LitElement {

89

disabled = false;

90

primary = true;

91

loading = false;

92

size = "medium";

93

94

render() {

95

const classes = {

96

'btn': true,

97

'btn-primary': this.primary,

98

'btn-secondary': !this.primary,

99

'btn-disabled': this.disabled,

100

'btn-loading': this.loading,

101

[`btn-${this.size}`]: this.size

102

};

103

104

return html`

105

<button class=${classMap(classes)} ?disabled=${this.disabled}>

106

${this.loading ? 'Loading...' : 'Click me'}

107

</button>

108

`;

109

}

110

}

111

```

112

113

### Style Map Directive

114

115

Conditionally applies CSS styles based on an object map.

116

117

```typescript { .api }

118

/**

119

* Sets CSS styles on an element based on a map of style properties to values

120

* @param styleInfo Object mapping CSS properties to values

121

* @returns DirectiveResult for conditional style application

122

*/

123

function styleMap(styleInfo: StyleInfo): DirectiveResult<typeof StyleMapDirective>;

124

125

interface StyleInfo {

126

[name: string]: string | number | undefined | null;

127

}

128

```

129

130

**Usage Examples:**

131

132

```typescript

133

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

134

135

class ProgressBar extends LitElement {

136

progress = 50; // 0-100

137

color = "#007bff";

138

height = 20;

139

140

render() {

141

const barStyles = {

142

width: `${this.progress}%`,

143

height: `${this.height}px`,

144

backgroundColor: this.color,

145

transition: 'width 0.3s ease'

146

};

147

148

const containerStyles = {

149

width: '100%',

150

height: `${this.height}px`,

151

backgroundColor: '#e9ecef',

152

borderRadius: '4px',

153

overflow: 'hidden'

154

};

155

156

return html`

157

<div style=${styleMap(containerStyles)}>

158

<div style=${styleMap(barStyles)}></div>

159

</div>

160

<p>${this.progress}% complete</p>

161

`;

162

}

163

}

164

```

165

166

### When Directive

167

168

Conditionally renders content based on a boolean condition.

169

170

```typescript { .api }

171

/**

172

* Conditionally renders one of two templates based on a condition

173

* @param condition Boolean condition to evaluate

174

* @param trueCase Template to render when condition is true

175

* @param falseCase Template to render when condition is false

176

* @returns Rendered template or nothing

177

*/

178

function when<T, F>(

179

condition: boolean,

180

trueCase: () => T,

181

falseCase?: () => F

182

): T | F | typeof nothing;

183

```

184

185

**Usage Examples:**

186

187

```typescript

188

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

189

190

class ConditionalElement extends LitElement {

191

isLoggedIn = false;

192

userName = "Alice";

193

hasPermission = true;

194

195

render() {

196

return html`

197

<div>

198

${when(

199

this.isLoggedIn,

200

() => html`

201

<div class="user-area">

202

<h2>Welcome, ${this.userName}!</h2>

203

${when(

204

this.hasPermission,

205

() => html`<button>Admin Panel</button>`,

206

() => html`<p>Limited access</p>`

207

)}

208

</div>

209

`,

210

() => html`

211

<div class="login-area">

212

<h2>Please log in</h2>

213

<button>Login</button>

214

</div>

215

`

216

)}

217

</div>

218

`;

219

}

220

}

221

```

222

223

### Choose Directive

224

225

Renders different templates based on a switch-like pattern.

226

227

```typescript { .api }

228

/**

229

* Renders different templates based on a value, like a switch statement

230

* @param value Value to switch on

231

* @param cases Object mapping values to template functions

232

* @param defaultCase Default template when no case matches

233

* @returns Rendered template based on value

234

*/

235

function choose<T, V>(

236

value: T,

237

cases: Record<string, () => V>,

238

defaultCase?: () => V

239

): V | typeof nothing;

240

```

241

242

**Usage Examples:**

243

244

```typescript

245

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

246

247

type Status = 'loading' | 'success' | 'error' | 'idle';

248

249

class StatusElement extends LitElement {

250

status: Status = 'idle';

251

message = '';

252

253

render() {

254

return html`

255

<div class="status-display">

256

${choose(this.status, {

257

loading: () => html`

258

<div class="spinner"></div>

259

<p>Loading...</p>

260

`,

261

success: () => html`

262

<div class="success-icon">βœ“</div>

263

<p>Success! ${this.message}</p>

264

`,

265

error: () => html`

266

<div class="error-icon">βœ—</div>

267

<p>Error: ${this.message}</p>

268

`,

269

idle: () => html`

270

<p>Ready to start</p>

271

<button @click=${this._start}>Start</button>

272

`

273

})}

274

</div>

275

`;

276

}

277

278

private _start() {

279

this.status = 'loading';

280

// Simulate async operation

281

setTimeout(() => {

282

this.status = Math.random() > 0.5 ? 'success' : 'error';

283

this.message = this.status === 'success' ? 'Operation completed' : 'Something went wrong';

284

}, 2000);

285

}

286

}

287

```

288

289

### If Defined Directive

290

291

Only sets an attribute if the value is defined (not null or undefined).

292

293

```typescript { .api }

294

/**

295

* Sets an attribute only if the value is defined (not null or undefined)

296

* @param value Value to conditionally set

297

* @returns Value or nothing if undefined/null

298

*/

299

function ifDefined(value: unknown): unknown | typeof nothing;

300

```

301

302

**Usage Examples:**

303

304

```typescript

305

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

306

307

class ImageElement extends LitElement {

308

src?: string;

309

alt?: string;

310

title?: string;

311

width?: number;

312

height?: number;

313

314

render() {

315

return html`

316

<img

317

src=${ifDefined(this.src)}

318

alt=${ifDefined(this.alt)}

319

title=${ifDefined(this.title)}

320

width=${ifDefined(this.width)}

321

height=${ifDefined(this.height)}

322

/>

323

`;

324

}

325

}

326

327

// Only defined attributes will be set:

328

// <img src="image.jpg" alt="Description" /> (title, width, height omitted)

329

```

330

331

### Guard Directive

332

333

Prevents re-evaluation of expensive templates unless dependencies change.

334

335

```typescript { .api }

336

/**

337

* Guards template re-evaluation by checking if dependencies have changed

338

* @param value Value or values to watch for changes

339

* @param fn Function that returns the template to render

340

* @returns Guarded template result

341

*/

342

function guard(value: unknown, fn: () => unknown): DirectiveResult<typeof GuardDirective>;

343

```

344

345

**Usage Examples:**

346

347

```typescript

348

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

349

350

class ExpensiveList extends LitElement {

351

data: any[] = [];

352

sortOrder = 'asc';

353

filterText = '';

354

355

private _processData() {

356

console.log('Processing data (expensive operation)');

357

return this.data

358

.filter(item => item.name.toLowerCase().includes(this.filterText.toLowerCase()))

359

.sort((a, b) => {

360

const order = this.sortOrder === 'asc' ? 1 : -1;

361

return a.name.localeCompare(b.name) * order;

362

});

363

}

364

365

render() {

366

return html`

367

<div>

368

<input

369

@input=${(e: any) => this.filterText = e.target.value}

370

placeholder="Filter..."

371

/>

372

<button @click=${() => this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc'}>

373

Sort ${this.sortOrder === 'asc' ? '↓' : '↑'}

374

</button>

375

376

<!-- Only re-process when data, sortOrder, or filterText change -->

377

${guard([this.data, this.sortOrder, this.filterText], () => {

378

const processedData = this._processData();

379

return html`

380

<ul>

381

${processedData.map(item => html`<li>${item.name}</li>`)}

382

</ul>

383

`;

384

})}

385

</div>

386

`;

387

}

388

}

389

```

390

391

### Cache Directive

392

393

Caches template results based on a key for performance optimization.

394

395

```typescript { .api }

396

/**

397

* Caches template results based on a value/key

398

* @param value Key value for caching

399

* @returns Cached directive result

400

*/

401

function cache(value: unknown): DirectiveResult<typeof CacheDirective>;

402

```

403

404

**Usage Examples:**

405

406

```typescript

407

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

408

409

class TabContainer extends LitElement {

410

activeTab = 'tab1';

411

tabs = ['tab1', 'tab2', 'tab3'];

412

413

private _renderTabContent(tabId: string) {

414

// Expensive template rendering

415

console.log(`Rendering content for ${tabId}`);

416

return html`

417

<div class="tab-content">

418

<h3>Content for ${tabId}</h3>

419

<p>This is expensive content that benefits from caching.</p>

420

<ul>

421

${Array.from({length: 100}, (_, i) => html`<li>Item ${i + 1}</li>`)}

422

</ul>

423

</div>

424

`;

425

}

426

427

render() {

428

return html`

429

<div class="tabs">

430

${this.tabs.map(tab => html`

431

<button

432

class=${tab === this.activeTab ? 'active' : ''}

433

@click=${() => this.activeTab = tab}

434

>

435

${tab}

436

</button>

437

`)}

438

</div>

439

440

<!-- Cache expensive content for each tab -->

441

${cache(this.activeTab)}

442

${this.activeTab === 'tab1' ? this._renderTabContent('tab1') : ''}

443

${this.activeTab === 'tab2' ? this._renderTabContent('tab2') : ''}

444

${this.activeTab === 'tab3' ? this._renderTabContent('tab3') : ''}

445

`;

446

}

447

}

448

```

449

450

### Live Directive

451

452

Forces a property binding to always update, even if the value hasn't changed.

453

454

```typescript { .api }

455

/**

456

* Forces live binding that always updates regardless of value changes

457

* @param value Value to bind with live updates

458

* @returns Live binding directive result

459

*/

460

function live(value: unknown): DirectiveResult<typeof LiveDirective>;

461

```

462

463

**Usage Examples:**

464

465

```typescript

466

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

467

468

class InputElement extends LitElement {

469

value = '';

470

471

render() {

472

return html`

473

<div>

474

<!-- Regular binding might not update if user modifies input -->

475

<input .value=${this.value} @input=${this._updateValue} />

476

477

<!-- Live binding ensures input always reflects property value -->

478

<input .value=${live(this.value)} @input=${this._updateValue} />

479

480

<button @click=${this._reset}>Reset</button>

481

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

482

</div>

483

`;

484

}

485

486

private _updateValue(e: Event) {

487

this.value = (e.target as HTMLInputElement).value;

488

}

489

490

private _reset() {

491

this.value = '';

492

// Live binding ensures input is cleared even if user has modified it

493

}

494

}

495

```

496

497

### Ref Directive

498

499

Creates references to DOM elements for imperative access.

500

501

```typescript { .api }

502

/**

503

* Creates a reference to a DOM element

504

* @param callback Optional callback function called with the element

505

* @returns Ref directive result

506

*/

507

function ref(callback?: (element?: Element) => void): DirectiveResult<typeof RefDirective>;

508

```

509

510

**Usage Examples:**

511

512

```typescript

513

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

514

515

class RefElement extends LitElement {

516

private inputRef = createRef<HTMLInputElement>();

517

private canvasRef = createRef<HTMLCanvasElement>();

518

519

render() {

520

return html`

521

<div>

522

<input ${ref(this.inputRef)} placeholder="Focus me" />

523

<canvas ${ref(this.canvasRef)} width="200" height="100"></canvas>

524

<button @click=${this._focusInput}>Focus Input</button>

525

<button @click=${this._drawOnCanvas}>Draw</button>

526

</div>

527

`;

528

}

529

530

private _focusInput() {

531

this.inputRef.value?.focus();

532

}

533

534

private _drawOnCanvas() {

535

const canvas = this.canvasRef.value;

536

if (canvas) {

537

const ctx = canvas.getContext('2d');

538

if (ctx) {

539

ctx.fillStyle = 'blue';

540

ctx.fillRect(10, 10, 100, 50);

541

ctx.fillStyle = 'white';

542

ctx.font = '16px Arial';

543

ctx.fillText('Hello!', 20, 35);

544

}

545

}

546

}

547

}

548

```

549

550

### Until Directive

551

552

Renders placeholder content until a promise resolves.

553

554

```typescript { .api }

555

/**

556

* Renders default content until a promise resolves, then renders the resolved value

557

* @param promise Promise to wait for

558

* @param defaultContent Content to show while waiting

559

* @returns Until directive result

560

*/

561

function until(promise: Promise<unknown>, ...defaultContent: unknown[]): DirectiveResult<typeof UntilDirective>;

562

```

563

564

**Usage Examples:**

565

566

```typescript

567

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

568

569

class AsyncElement extends LitElement {

570

private async _loadUserData(userId: number) {

571

// Simulate API call

572

await new Promise(resolve => setTimeout(resolve, 2000));

573

return { id: userId, name: `User ${userId}`, email: `user${userId}@example.com` };

574

}

575

576

private async _loadPosts() {

577

await new Promise(resolve => setTimeout(resolve, 1500));

578

return ['Post 1', 'Post 2', 'Post 3'];

579

}

580

581

render() {

582

const userData = this._loadUserData(123);

583

const posts = this._loadPosts();

584

585

return html`

586

<div>

587

<h2>User Profile</h2>

588

589

<!-- Show loading message until user data loads -->

590

${until(

591

userData.then(user => html`

592

<div class="user-info">

593

<h3>${user.name}</h3>

594

<p>Email: ${user.email}</p>

595

<p>ID: ${user.id}</p>

596

</div>

597

`),

598

html`<p>Loading user data...</p>`

599

)}

600

601

<h3>Recent Posts</h3>

602

603

<!-- Show spinner until posts load -->

604

${until(

605

posts.then(postList => html`

606

<ul>

607

${postList.map(post => html`<li>${post}</li>`)}

608

</ul>

609

`),

610

html`<div class="spinner">Loading posts...</div>`

611

)}

612

</div>

613

`;

614

}

615

}

616

```

617

618

### Unsafe HTML/SVG Directives

619

620

Renders untrusted HTML/SVG content (use with extreme caution).

621

622

```typescript { .api }

623

/**

624

* Renders HTML string as actual HTML (bypasses sanitization)

625

* WARNING: Only use with trusted content to prevent XSS attacks

626

* @param html HTML string to render

627

* @returns Unsafe HTML directive result

628

*/

629

function unsafeHTML(html: string): DirectiveResult<typeof UnsafeHTMLDirective>;

630

631

/**

632

* Renders SVG string as actual SVG (bypasses sanitization)

633

* WARNING: Only use with trusted content to prevent XSS attacks

634

* @param svg SVG string to render

635

* @returns Unsafe SVG directive result

636

*/

637

function unsafeSVG(svg: string): DirectiveResult<typeof UnsafeSVGDirective>;

638

```

639

640

**Usage Examples:**

641

642

```typescript

643

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

644

645

class UnsafeContentElement extends LitElement {

646

// ONLY use with trusted content!

647

trustedHtml = '<strong>Bold</strong> and <em>italic</em> text';

648

trustedSvg = '<circle cx="50" cy="50" r="25" fill="red" />';

649

650

render() {

651

return html`

652

<div>

653

<h3>Trusted HTML Content:</h3>

654

<div>${unsafeHTML(this.trustedHtml)}</div>

655

656

<h3>Trusted SVG Content:</h3>

657

<svg width="100" height="100">

658

${unsafeSVG(this.trustedSvg)}

659

</svg>

660

</div>

661

`;

662

}

663

}

664

```

665

666

### Unsafe MathML Directive

667

668

Renders MathML string as actual MathML (bypasses sanitization).

669

670

```typescript { .api }

671

/**

672

* Renders MathML string as actual MathML (bypasses sanitization)

673

* WARNING: Only use with trusted content to prevent XSS attacks

674

* @param mathml MathML string to render

675

* @returns Unsafe MathML directive result

676

*/

677

function unsafeMathML(mathml: string): DirectiveResult<typeof UnsafeMathMLDirective>;

678

```

679

680

### Async Append Directive

681

682

Appends values from an async iterable to the DOM.

683

684

```typescript { .api }

685

/**

686

* Appends values from an async iterable, preserving previous values

687

* @param asyncIterable Async iterable of values to append

688

* @returns Async append directive result

689

*/

690

function asyncAppend(asyncIterable: AsyncIterable<unknown>): DirectiveResult<typeof AsyncAppendDirective>;

691

```

692

693

**Usage Examples:**

694

695

```typescript

696

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

697

698

class StreamingElement extends LitElement {

699

async *generateData() {

700

for (let i = 0; i < 10; i++) {

701

await new Promise(resolve => setTimeout(resolve, 1000));

702

yield html`<div>Item ${i + 1} - ${new Date().toLocaleTimeString()}</div>`;

703

}

704

}

705

706

render() {

707

return html`

708

<div>

709

<h3>Streaming Data (Append):</h3>

710

<div class="stream">

711

${asyncAppend(this.generateData())}

712

</div>

713

</div>

714

`;

715

}

716

}

717

```

718

719

### Async Replace Directive

720

721

Replaces content with values from an async iterable.

722

723

```typescript { .api }

724

/**

725

* Replaces content with values from an async iterable

726

* @param asyncIterable Async iterable of values to render

727

* @returns Async replace directive result

728

*/

729

function asyncReplace(asyncIterable: AsyncIterable<unknown>): DirectiveResult<typeof AsyncReplaceDirective>;

730

```

731

732

**Usage Examples:**

733

734

```typescript

735

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

736

737

class CountdownElement extends LitElement {

738

async *countdown(from: number) {

739

for (let i = from; i >= 0; i--) {

740

yield html`<div class="countdown">${i}</div>`;

741

if (i > 0) {

742

await new Promise(resolve => setTimeout(resolve, 1000));

743

}

744

}

745

yield html`<div class="done">πŸŽ‰ Done!</div>`;

746

}

747

748

render() {

749

return html`

750

<div>

751

<h3>Countdown Timer:</h3>

752

${asyncReplace(this.countdown(5))}

753

</div>

754

`;

755

}

756

}

757

```

758

759

### Map Directive

760

761

Maps over an iterable with a template function.

762

763

```typescript { .api }

764

/**

765

* Maps over an iterable, applying a template function to each item

766

* @param items Iterable to map over

767

* @param template Function to apply to each item

768

* @returns Map directive result

769

*/

770

function map<T>(

771

items: Iterable<T>,

772

template: (item: T, index: number) => unknown

773

): DirectiveResult<typeof MapDirective>;

774

```

775

776

**Usage Examples:**

777

778

```typescript

779

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

780

781

class MappedList extends LitElement {

782

items = ['apple', 'banana', 'cherry'];

783

users = [

784

{ id: 1, name: 'Alice' },

785

{ id: 2, name: 'Bob' }

786

];

787

788

render() {

789

return html`

790

<div>

791

<h3>Simple List:</h3>

792

<ul>

793

${map(this.items, (item, index) => html`

794

<li>${index + 1}. ${item}</li>

795

`)}

796

</ul>

797

798

<h3>User Cards:</h3>

799

<div class="users">

800

${map(this.users, (user) => html`

801

<div class="user-card">

802

<h4>${user.name}</h4>

803

<p>ID: ${user.id}</p>

804

</div>

805

`)}

806

</div>

807

</div>

808

`;

809

}

810

}

811

```

812

813

### Join Directive

814

815

Joins rendered items with a separator.

816

817

```typescript { .api }

818

/**

819

* Joins rendered items with a separator

820

* @param items Iterable of items to join

821

* @param joiner Separator to place between items

822

* @returns Join directive result

823

*/

824

function join<T>(

825

items: Iterable<T>,

826

joiner: unknown

827

): DirectiveResult<typeof JoinDirective>;

828

```

829

830

**Usage Examples:**

831

832

```typescript

833

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

834

835

class JoinedList extends LitElement {

836

tags = ['javascript', 'typescript', 'lit-element'];

837

breadcrumbs = ['Home', 'Products', 'Electronics', 'Phones'];

838

839

render() {

840

return html`

841

<div>

842

<h3>Tags:</h3>

843

<p>

844

${join(

845

this.tags.map(tag => html`<span class="tag">${tag}</span>`),

846

html`<span class="separator"> β€’ </span>`

847

)}

848

</p>

849

850

<h3>Breadcrumbs:</h3>

851

<nav>

852

${join(

853

this.breadcrumbs.map(crumb => html`<a href="#">${crumb}</a>`),

854

html`<span> / </span>`

855

)}

856

</nav>

857

858

<h3>Simple Join:</h3>

859

<p>${join(this.breadcrumbs, ' > ')}</p>

860

</div>

861

`;

862

}

863

}

864

```

865

866

### Range Directive

867

868

Generates a range of numbers for iteration.

869

870

```typescript { .api }

871

/**

872

* Generates a range of numbers

873

* @param startOrEnd Start value (if end provided) or end value (if only parameter)

874

* @param end End value (exclusive)

875

* @param step Step size (default: 1)

876

* @returns Range directive result

877

*/

878

function range(startOrEnd: number, end?: number, step?: number): Iterable<number>;

879

```

880

881

**Usage Examples:**

882

883

```typescript

884

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

885

886

class RangeElement extends LitElement {

887

render() {

888

return html`

889

<div>

890

<h3>Simple Range (0 to 5):</h3>

891

<ul>

892

${map(range(5), (n) => html`<li>Item ${n}</li>`)}

893

</ul>

894

895

<h3>Custom Range (10 to 20, step 2):</h3>

896

<ul>

897

${map(range(10, 20, 2), (n) => html`<li>Number ${n}</li>`)}

898

</ul>

899

900

<h3>Grid (3x3):</h3>

901

<div class="grid">

902

${map(range(3), (row) => html`

903

<div class="row">

904

${map(range(3), (col) => html`

905

<div class="cell">${row},${col}</div>

906

`)}

907

</div>

908

`)}

909

</div>

910

</div>

911

`;

912

}

913

}

914

```

915

916

### Keyed Directive

917

918

Forces re-rendering when a key changes.

919

920

```typescript { .api }

921

/**

922

* Associates a key with content, forcing re-render when key changes

923

* @param key Key value to associate with content

924

* @param value Content to render

925

* @returns Keyed directive result

926

*/

927

function keyed(key: unknown, value: unknown): DirectiveResult<typeof KeyedDirective>;

928

```

929

930

**Usage Examples:**

931

932

```typescript

933

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

934

935

class KeyedElement extends LitElement {

936

currentUser = { id: 1, name: 'Alice' };

937

refreshKey = 0;

938

939

private _switchUser() {

940

this.currentUser = this.currentUser.id === 1

941

? { id: 2, name: 'Bob' }

942

: { id: 1, name: 'Alice' };

943

}

944

945

private _refresh() {

946

this.refreshKey++;

947

}

948

949

render() {

950

return html`

951

<div>

952

<button @click=${this._switchUser}>Switch User</button>

953

<button @click=${this._refresh}>Force Refresh</button>

954

955

<!-- Force re-render when user changes -->

956

${keyed(this.currentUser.id, html`

957

<div class="user-profile">

958

<h3>${this.currentUser.name}</h3>

959

<input placeholder="User-specific input" />

960

<p>Profile loaded at: ${new Date().toLocaleTimeString()}</p>

961

</div>

962

`)}

963

964

<!-- Force re-render when refresh key changes -->

965

${keyed(this.refreshKey, html`

966

<div class="refresh-content">

967

<p>Refreshed content: ${Math.random()}</p>

968

</div>

969

`)}

970

</div>

971

`;

972

}

973

}

974

```

975

976

### Template Content Directive

977

978

Renders content from an HTMLTemplateElement.

979

980

```typescript { .api }

981

/**

982

* Renders the content of an HTMLTemplateElement

983

* @param templateElement HTMLTemplateElement to render

984

* @returns Template content directive result

985

*/

986

function templateContent(templateElement: HTMLTemplateElement): DirectiveResult<typeof TemplateContentDirective>;

987

```

988

989

**Usage Examples:**

990

991

```typescript

992

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

993

994

class TemplateContentElement extends LitElement {

995

private _getTemplate() {

996

const template = document.createElement('template');

997

template.innerHTML = `

998

<div style="border: 1px solid #ccc; padding: 16px;">

999

<h4>Template Content</h4>

1000

<p>This content came from an HTMLTemplateElement</p>

1001

<button>Template Button</button>

1002

</div>

1003

`;

1004

return template;

1005

}

1006

1007

render() {

1008

return html`

1009

<div>

1010

<h3>Rendered Template:</h3>

1011

${templateContent(this._getTemplate())}

1012

</div>

1013

`;

1014

}

1015

}

1016

```