Creates a mutable ref object that can hold a reference to a DOM node or component instance.
Signature
function createRef<T = any>(): RefObject<T>
Type Parameters
The type of the value the ref will hold (e.g., HTMLDivElement, HTMLInputElement).
Return Value
Returns a RefObject<T> with the following structure:
interface RefObject<T> {
current: T | null;
}
The current property:
- Initially set to
null
- Updated by Preact when the component mounts
- Set to the DOM node or component instance
- Reset to
null when the component unmounts
Description
createRef creates a mutable object whose current property is initialized to null. This object persists for the lifetime of the component and is used to access DOM nodes or component instances directly.
Refs are useful for:
- Managing focus, text selection, or media playback
- Triggering imperative animations
- Integrating with third-party DOM libraries
- Accessing component methods
Implementation
The function is implemented in src/create-element.js:73:
export function createRef() {
return { current: NULL };
}
The implementation is simple - it returns a plain object with a current property initialized to null.
Usage Examples
Accessing DOM Elements
import { createRef, Component } from 'preact';
class AutoFocusInput extends Component {
inputRef = createRef();
componentDidMount() {
// Focus the input after mount
this.inputRef.current.focus();
}
render() {
return <input ref={this.inputRef} type="text" />;
}
}
With Function Components
import { createRef } from 'preact';
import { useEffect } from 'preact/hooks';
function VideoPlayer() {
const videoRef = createRef();
useEffect(() => {
// Play video on mount
videoRef.current.play();
}, []);
const handlePause = () => {
videoRef.current.pause();
};
return (
<div>
<video ref={videoRef} src="video.mp4" />
<button onClick={handlePause}>Pause</button>
</div>
);
}
Measuring DOM Elements
import { createRef, Component } from 'preact';
class MeasureDiv extends Component {
divRef = createRef();
measureElement = () => {
const element = this.divRef.current;
console.log('Width:', element.offsetWidth);
console.log('Height:', element.offsetHeight);
console.log('Position:', element.getBoundingClientRect());
}
render() {
return (
<div>
<div ref={this.divRef} style={{ width: '200px', height: '100px' }}>
Measure me!
</div>
<button onClick={this.measureElement}>Get Measurements</button>
</div>
);
}
}
Managing Focus
import { createRef, Component } from 'preact';
class Form extends Component {
nameRef = createRef();
emailRef = createRef();
handleNameSubmit = (e) => {
e.preventDefault();
// Move focus to email field
this.emailRef.current.focus();
}
render() {
return (
<form>
<input
ref={this.nameRef}
placeholder="Name"
onKeyPress={(e) => e.key === 'Enter' && this.handleNameSubmit(e)}
/>
<input
ref={this.emailRef}
type="email"
placeholder="Email"
/>
</form>
);
}
}
With TypeScript
import { createRef, Component } from 'preact';
class TypedInput extends Component {
// Specify the element type
inputRef = createRef<HTMLInputElement>();
buttonRef = createRef<HTMLButtonElement>();
componentDidMount() {
// TypeScript knows inputRef.current is HTMLInputElement | null
this.inputRef.current?.focus();
// Access type-specific properties
const value = this.inputRef.current?.value;
const isDisabled = this.buttonRef.current?.disabled;
}
render() {
return (
<div>
<input ref={this.inputRef} />
<button ref={this.buttonRef}>Submit</button>
</div>
);
}
}
Text Selection
import { createRef, Component } from 'preact';
class SelectableText extends Component {
textRef = createRef();
selectAll = () => {
const input = this.textRef.current;
input.select();
}
selectRange = (start, end) => {
const input = this.textRef.current;
input.setSelectionRange(start, end);
input.focus();
}
render() {
return (
<div>
<input
ref={this.textRef}
type="text"
defaultValue="Select this text"
/>
<button onClick={this.selectAll}>Select All</button>
<button onClick={() => this.selectRange(0, 6)}>Select First Word</button>
</div>
);
}
}
Integrating Third-Party Libraries
import { createRef, Component } from 'preact';
import * as d3 from 'd3';
class D3Chart extends Component {
svgRef = createRef();
componentDidMount() {
// Use D3 with the DOM element
const svg = d3.select(this.svgRef.current);
svg.append('circle')
.attr('cx', 50)
.attr('cy', 50)
.attr('r', 40)
.attr('fill', 'blue');
}
render() {
return <svg ref={this.svgRef} width="100" height="100" />;
}
}
Multiple Refs
import { createRef, Component } from 'preact';
class MultipleRefs extends Component {
firstRef = createRef();
secondRef = createRef();
thirdRef = createRef();
focusNext = (currentRef, nextRef) => {
const current = currentRef.current;
if (current.value.length >= current.maxLength) {
nextRef.current.focus();
}
}
render() {
return (
<div>
<input
ref={this.firstRef}
maxLength={3}
onChange={() => this.focusNext(this.firstRef, this.secondRef)}
/>
<input
ref={this.secondRef}
maxLength={3}
onChange={() => this.focusNext(this.secondRef, this.thirdRef)}
/>
<input ref={this.thirdRef} maxLength={3} />
</div>
);
}
}
Forwarding Refs
import { createRef, forwardRef } from 'preact';
// Component that forwards its ref to an internal element
const FancyInput = forwardRef((props, ref) => (
<div className="fancy-input">
<input ref={ref} {...props} />
</div>
));
function Parent() {
const inputRef = createRef();
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<FancyInput ref={inputRef} placeholder="Forwarded ref" />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}
Ref vs useRef Hook
For function components, prefer the useRef hook:
// Class component - use createRef
import { createRef, Component } from 'preact';
class MyClass extends Component {
myRef = createRef();
render() {
return <div ref={this.myRef} />;
}
}
// Function component - use useRef
import { useRef } from 'preact/hooks';
function MyFunction() {
const myRef = useRef(null);
return <div ref={myRef} />;
}
Key differences:
createRef creates a new object every time
useRef persists the same object across re-renders
useRef is preferred for function components
Common Patterns
Conditional Refs
import { createRef, Component } from 'preact';
class ConditionalRef extends Component {
inputRef = createRef();
componentDidUpdate(prevProps) {
if (this.props.shouldFocus && !prevProps.shouldFocus) {
this.inputRef.current?.focus();
}
}
render() {
return (
<input
ref={this.inputRef}
type="text"
placeholder="Type here"
/>
);
}
}
Callback Refs
For more control, use callback refs instead:
import { Component } from 'preact';
class CallbackRefExample extends Component {
setInputRef = (element) => {
this.inputElement = element;
if (element) {
element.focus();
}
}
render() {
return <input ref={this.setInputRef} />;
}
}
Best Practices
- Check for null: Always check if
ref.current exists before using it
if (this.myRef.current) {
this.myRef.current.focus();
}
// Or use optional chaining
this.myRef.current?.focus();
-
Don’t overuse refs: Use refs only when necessary. For most cases, data flow through props is preferred
-
Avoid string refs: Preact supports string refs for compatibility, but object refs are preferred
// ❌ Avoid string refs
<input ref="myInput" />
// ✅ Use object refs
<input ref={this.myRef} />
- Function components: Use
useRef hook instead of createRef in function components
useRef hook - Ref hook for function components
forwardRef - Forward refs through components
Component - Class component API
h - Create elements with refs