Last Updated: November 21, 2025
Component Basics
class MyElement extends LitElement
Create Lit component class
@customElement('my-element')
Register custom element decorator
render() { return html`...` }
Define component template
html`<div>Hello</div>`
Create HTML template literal
css`selector { prop: value }`
Define scoped styles
static styles = css`...`
Add styles to component
static styles = [style1, style2]
Compose multiple style sheets
Property Decorators
@property({ type: String })
Declare reactive public property
@property({ type: Number })
Number property with type conversion
@property({ type: Boolean })
Boolean property (attribute presence)
@property({ type: Array })
Array property
@property({ type: Object })
Object property
@property({ attribute: false })
Property without HTML attribute
@property({ reflect: true })
Reflect property to attribute
@property({ converter: myConverter })
Custom attribute converter
@state()
Private reactive state (no attribute)
@property({ hasChanged: (n, o) => {} })
Custom change detection
Template Expressions
${expression}
Insert dynamic content
${condition ? html`yes` : html`no`}
Conditional rendering
${items.map(i => html`...`)}
Render list with map
?disabled=${!active}
Boolean attribute binding
.value=${data}
Property binding (not attribute)
@click=${this.handleClick}
Event listener binding
class=${classMap(classes)}
Dynamic class mapping
style=${styleMap(styles)}
Dynamic style mapping
${guard([dep], () => html`...`)}
Only re-render when deps change
${cache(condition ? html`A` : html`B`)}
Cache template instances
Directives
repeat(items, key, template)
Efficient list rendering with keys
when(condition, trueCase, falseCase)
Conditional with both branches
choose(value, cases, defaultCase)
Switch-like conditional
map(items, (item, i) => html`...`)
Map directive for lists
range(count)
Generate range of numbers
ifDefined(value)
Only set attribute if defined
live(value)
Check live DOM value (for inputs)
unsafeHTML(html)
Render raw HTML (use carefully)
unsafeSVG(svg)
Render raw SVG
until(promise, loading)
Render promise result with loading state
Lifecycle Methods
connectedCallback()
Called when element added to DOM
disconnectedCallback()
Called when element removed from DOM
firstUpdated(changedProps)
Called after first render
updated(changedProps)
Called after each update
willUpdate(changedProps)
Called before update/render
shouldUpdate(changedProps)
Control whether to update
attributeChangedCallback()
Called when attribute changes
requestUpdate()
Manually trigger update
performUpdate()
Override update lifecycle
updateComplete
Promise that resolves after update
Query Decorators
@query('#id')
Query single element in shadow DOM
@queryAll('.class')
Query all matching elements
@queryAsync('#id')
Async query (resolves after update)
@queryAssignedElements()
Query slotted elements
@queryAssignedNodes()
Query slotted nodes (including text)
Events
@event() myEvent!: EventEmitter
Declare custom event
this.dispatchEvent(new Event('my-event'))
Dispatch custom event
new CustomEvent('name', { detail })
Dispatch event with data
{ bubbles: true, composed: true }
Make event cross shadow boundary
@click=${this.handler}
Bind event in template
@click=${(e: Event) => {}}
Inline event handler
Reactive Controllers
class MyController implements ReactiveController
Create reactive controller
hostConnected()
Controller lifecycle hook
hostDisconnected()
Cleanup when host disconnects
hostUpdate()
Called before host updates
hostUpdated()
Called after host updates
this.host.requestUpdate()
Trigger host update from controller
this.addController(controller)
Register controller in component
Context API
createContext('key')
Create context identifier
@provide({ context: myContext })
Provide context value
@consume({ context: myContext })
Consume context value
@consume({ subscribe: true })
Subscribe to context changes
Best Practices
static shadowRootOptions = { mode: 'open' }
Configure shadow DOM options
@property({ type: String }) name = 'default'
Set default property values
await this.updateComplete
Wait for render to complete
nothing
Render nothing (from lit/html)
noChange
Keep previous value (directives)
static getMetaConfig() { return { ... } }
Define component metadata
💡 Pro Tip:
Use @state() for private component state and @property() for public API. Leverage reactive controllers to extract and reuse complex logic across components!