Cossack provides a way to directly reference DOM elements within your components using the @Ref decorator, similar to React's useRef hook. This is useful for managing focus, text selection, or integrating with third-party DOM libraries.
Basic Usage
To use a ref, decorate a property with @Ref() and type it as RefObject<HTMLElement>. Then, bind it to an element in your template using the ref attribute.
import { Cossack, Page, Ref, html, type RefObject } from '@cossackframework/core'; @Page() export default class RefExample extends Cossack { @Ref() declare inputRef: RefObject<HTMLInputElement>; onMount() { // Access the DOM element via .value this.inputRef.value?.focus(); } render() { return html` <h1>Focus Me</h1> <input type="text" ref=${this.inputRef} placeholder="I will be focused automatically" /> `; } }
Note: When using TypeScript with
useDefineForClassFields: true(the default in Vite), you must use thedeclarekeyword for properties decorated with@Ref. This prevents the compiler from emitting a property initializer that would overwrite the decorator's logic.
How it Works
- Declaration: The
@Ref()decorator initializes the property with a stable object{ value: undefined }. - Binding: When the template is rendered, the
refattribute directive detects theRefObjectand assigns the DOM element to its.valueproperty. - Access: You can access the underlying DOM element in
onMount()or any subsequent method. Note thatthis.inputRef.valuewill beundefinedduring the initial server-side render.
Functional Refs
You can also pass a function to the ref attribute if you need more control or don't want to use the decorator.
render() { return html` <div ref=${(el: HTMLElement) => console.log('Element mounted:', el)}> Hello </div> `; }
Using with Multi-Interpolation
Refs work seamlessly alongside other attributes, including our new multi-interpolation support.
html` <input ref=${this.inputRef} style="color: ${this.color}; background: ${this.bg}" /> `