Unique Instances
By default, when you navigate to a different page or hide a component using If(), Retend destroys those components. If those components are shown again later, brand new ones are created from scratch.
This behavior is fine for static content, but interactive elements lose their internal state when recreated. A playing video stops, a canvas drawing disappears, and form inputs reset.
Unique Instances solve this by letting you create components that survive being moved around your application.
Creating Persistent Components
To make a component persistent, simply wrap its function in createUnique():
import { createUnique } from 'retend'; export const PersistentVideo = createUnique(() => { return ( <video src="https://example.com/video.mp4" controls autoplay> Your browser does not support the video tag. </video> ); });
Now, if you render <PersistentVideo /> on your Home page, and then the user navigates to the About page where <PersistentVideo /> is also rendered, the exact same component instance is seamlessly moved to the new page. The video will keep playing without skipping a beat.
Multiple Independent Instances
By default, every time you use a unique component, it shares the exact same underlying instance. If you render <PersistentVideo /> in two different places at the same time, the component will move to whichever one was rendered last.
If you want multiple, independent unique instances that can coexist on the screen at the same time, just give each one a unique id prop:
// Two separate video players, each with their own state <PersistentVideo id="camera-1" /> <PersistentVideo id="camera-2" />
Now camera-1 and camera-2 are completely independent persistent instances.
Passing Props
If your unique component needs to accept props, those props will be provided to your function as a single reactive Cell. This allows your persistent component to instantly react when it is moved to a new location with different props.
import { createUnique, Cell } from 'retend'; const UniquePanel = createUnique((props) => { // 1. Create a derived Cell so the title automatically updates const title = Cell.derived(() => props.get().title); return ( <div class="panel"> <h2>{title}</h2> <p>This panel persists as it moves.</p> </div> ); }); // If rendered here... <UniquePanel id="panel-1" title="First Title" /> // And then later moved here... <UniquePanel id="panel-1" title="Updated Title" /> // The exact same component instance is used, but the <h2> text updates instantly!
Saving and Restoring Scroll Position
While createUnique preserves the component instance, some DOM state like scroll position is reset when the component moves between locations.
You can use the onMove hook to explicitly save and restore state during moves:
import { createUnique, onMove, Cell } from 'retend'; const ScrollableArea = createUnique(() => { const ref = Cell.source<HTMLDivElement | null>(null); onMove(() => { const element = ref.get(); if (!element) return; const scrollPos = element.scrollTop; return () => { element.scrollTop = scrollPos; }; }); return ( <div ref={ref} style={{ height: '400px', overflow: 'auto' }}> <p>Lots of content here...</p> </div> ); });
The onMove hook runs before the unique component moves. Return a function from it to run after the move completes.
Unique instances are a powerful tool whenever you need components that maintain their internal state across navigation.