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
```