Concepts

Refs

Edit this page

Refs, or references, are a special attribute that can be attached to any element, and are used to reference a DOM element or a component instance. They are particularly useful when you need to access the DOM nodes directly or invoke methods on a component.


Accessing DOM elements

One way of accessing DOM elements is through element selectors such as document.querySelector or document.getElementById. Since elements in Solid can be added or removed from the DOM based on state, you need to wait until the element is attached to the DOM before accessing it. This can be done by using onSettled to wait until the element is attached to the DOM before accessing it:

Accessing DOM elements through element selectors is not recommended for this reason. As elements with the same selectors are added and removed from the DOM, the first element that matches the selector will be returned, which may not be the element you want.


JSX as a value

JSX can be used as a value and assigned to a variable when looking to directly access DOM elements.

function Component() {
const myElement = <p>My Element</p>
return <div>{myElement}</div>
}

This lets you create and access DOM elements similar to document.createElement but without having to wait until it is attached to the DOM. It can be used multiple times without having to worry about duplicate selectors.

The downside of this approach is that it separates the element and any child elements from the rest of the JSX structure. This makes the component's JSX structure more difficult to read and understand.


Refs in Solid

Solid provides a ref system to access DOM elements directly inside the JSX template, which keeps the structure of the elements intact.

To use ref, you declare a variable and use it as the ref attribute:

function Component() {
let myElement;
return (
<div>
<p ref={myElement}>My Element</p>
</div>
)
}

These assignments occur at creation time prior to the element being added to the DOM. If access to an element is needed before it is added to the DOM, you can use the callback form of ref:

<p ref={(el) => {
myElement = el // el is created but not yet added to the DOM
}}>
My Element
</p>

Signals as refs

Signals can also be used as refs. This is useful when you want to access the element directly, but the element may not exist when the component is first rendered, or may be removed from the DOM at some point.

function App() {
const [show, setShow] = createSignal(false)
let element!: HTMLParagraphElement
return (
<div>
<button onClick={() => setShow((isShown) => !isShown)}>Toggle</button>
<Show when={show()}>
<p ref={element}>This is the ref element</p>
</Show>
</div>
)
}

In this example, the paragraph element is only rendered when the show signal is true. When the component initializes, the paragraph element does not exist, so the element variable is not assigned. Once the show signal is set to true, the paragraph element is rendered, and the element variable is assigned to the paragraph element.

You can see a detailed view of the ref update lifecycle in this Solid playground example.


Forwarding refs

Forwarding refs is a technique that allows you to pass a ref from a parent component to a child component. This is useful when you want to access the DOM element of a child component from the parent component.

To forward a ref, you need to pass the ref to the child component, and then assign the ref to the child component's element.

When a child component receives a ref attribute from its parent, the ref is passed as a callback function. This is regardless of whether the parent passed it as a simple assignment or a callback.

Once the child component receives the ref, it can be assigned to the element that the child component wants to expose through the ref attribute. To access the ref in the child component, it is passed as a prop:

// Parent component
import { Canvas } from "./Canvas.jsx"
function ParentComponent() {
let canvasRef
const animateCanvas = () => {
// Manipulate the canvas using canvasRef...
}
return (
<div>
<Canvas ref={canvasRef} />
<button onClick={animateCanvas}>Animate Canvas</button>
</div>
)
}
// Child component
function Canvas(props) {
return (
<div className="canvas-container">
<canvas ref={props.ref} /> {/* Assign the ref to the canvas element */}
</div>
)
}

In this example, the canvas element is directly assigned the ref attribute through the props.ref variable. This forwards the reference to the parent component, giving it direct access to the canvas element.


Directives via ref factories

Directives allow the attachment of reusable behaviors to DOM elements. In Solid 2.0, they use the ref prop:

// 2.0: directive factory pattern
<input ref={autofocus} />
<button ref={tooltip({ content: "Save" })} />

Multiple directives can be composed using arrays:

<button ref={[autofocus, tooltip({ content: "Save" })]} />

Two-phase directive pattern

The recommended directive pattern is two-phase — matching the split effect model:

  1. Setup phase (owned): Create reactive primitives and subscriptions. Avoid imperative DOM mutation.
  2. Apply phase (unowned): Receives the element, performs DOM writes. No new primitives should be created.
function titleDirective(source) {
// Setup phase (owned): create primitives/subscriptions
let el;
createEffect(source, (value) => {
if (el) el.title = value;
});
// Apply phase (unowned): DOM writes happen here
return (nextEl) => {
el = nextEl;
el.title = source();
};
}

Used as:

<button ref={titleDirective(() => props.title)} />

To learn more about directives and how they work with TypeScript, refer to our TypeScript for Solid guide.

Report an issue with this page