Solid Presence
Solid Presence is a utility for SolidJS that manages an element's presence in the DOM, intelligently accounting for ongoing CSS animations or transitions before mounting or unmounting. It provides a `present` signal and a `state` variable (which can be `present`, `hiding`, or `hidden`) to offer fine-grained control over the element's lifecycle. Currently at version 0.2.0, it is developed as part of the Corvu UI primitives monorepo. While `solid-presence` itself hasn't received a dedicated individual release in the most recent changelogs provided, other Corvu packages are frequently updated, indicating an active and continuous development cycle for the ecosystem it belongs to. Its primary differentiator lies in its ability to prevent common issues like flickering or premature DOM removal during animated transitions, offering a robust solution for components such as animated dialogs, tooltips, or transitions where smooth presence management is critical.
Common errors
-
TypeError: Cannot read properties of null (reading 'style') in createPresence
cause The `element` signal provided to `createPresence` is `null` when the utility attempts to access properties or methods on the DOM element. This usually happens if the ref has not yet been assigned to a mounted element, or the element is not rendered when `createPresence` tries to initialize.fixEnsure the `element` signal is only accessed after the DOM element it refers to has been mounted. Wrap the element requiring the ref within a `Show` component, and ensure the ref assignment (`ref={setDialogRef}`) happens correctly. -
Element disappears without animation even with CSS transitions
cause Although CSS transitions might be defined, `solid-presence` needs to be aware of the `transition-duration` or `animation-duration`. If the CSS transitions are not applied correctly, or if their duration is effectively zero, the utility will remove the element immediately upon `show` becoming false.fixDouble-check your CSS to ensure the element has valid `transition-property` and `transition-duration` (or `animation-duration`) rules that apply during its exit phase. Sometimes, the transition might be on a child element or a property not directly managed by `solid-presence`'s internal event listeners.
Warnings
- breaking As `solid-presence` is currently in an early pre-1.0 development stage (v0.2.0), future minor or major releases may introduce breaking changes to the API, including the signature of `createPresence`, its options, or the structure of the returned value.
- gotcha It is crucial that the `element` signal provided to `createPresence` correctly points to the actual DOM element whose presence is being managed. Incorrect ref assignment (e.g., the ref being `null` or pointing to a non-existent element) or attempting to manage an element not directly rendered by SolidJS can lead to unexpected behavior, where presence transitions may not resolve correctly.
- gotcha `solid-presence` relies on CSS `transitionend` or `animationend` events to determine when an element has finished its exit animation. If the managed element lacks defined CSS transitions/animations, or if their `transition-duration`/`animation-duration` properties are set to `0s`, the element might be removed from the DOM immediately, bypassing the intended animation and negating the utility's purpose.
Install
-
npm install solid-presence -
yarn add solid-presence -
pnpm add solid-presence
Imports
- createPresence
const createPresence = require('solid-presence')import createPresence from 'solid-presence'
Quickstart
import { createSignal, Show, Component } from 'solid-js';
import createPresence from 'solid-presence';
const DialogContent: Component<{ open?: boolean }> = (props) => {
const [dialogRef, setDialogRef] = createSignal<HTMLElement | null>(null);
const { present, state } = createPresence({
show: () => props.open,
element: dialogRef,
});
return (
<Show when={present()}>
<div
ref={setDialogRef}
// Example styling for visual feedback and animation awareness
style={{
transition: 'opacity 0.3s ease-in-out, transform 0.3s ease-in-out',
opacity: props.open ? 1 : 0,
transform: props.open ? 'translateY(0)' : 'translateY(-20px)',
background: 'lightgray',
padding: '20px',
border: '1px solid gray',
minWidth: '200px',
minHeight: '100px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
boxShadow: '0 4px 8px rgba(0,0,0,0.1)'
}}
>
<h2>Dialog Content</h2>
<p>Current state: <strong>{state()}</strong></p>
<p>This element is managed by solid-presence.</p>
</div>
</Show>
);
};
// To demonstrate in a runnable context:
const App: Component = () => {
const [isOpen, setIsOpen] = createSignal(false);
return (
<div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
<button
onClick={() => setIsOpen(!isOpen())}
style={{
padding: '10px 20px',
fontSize: '1em',
cursor: 'pointer',
marginBottom: '20px'
}}
>
{isOpen() ? 'Close Dialog' : 'Open Dialog'}
</button>
<DialogContent open={isOpen()} />
</div>
);
};
// In a full SolidJS application, you would mount the App component:
// import { render } from 'solid-js/web';
// render(() => <App />, document.getElementById('app')!);