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}"
/>
`