Socket
Book a DemoInstallSign in
Socket

fluidstate-preact

Package Overview
Dependencies
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fluidstate-preact

Library for using fine-grained reactivity state management library fluidstate in Preact

latest
Source
npmnpm
Version
1.0.0
Version published
Maintainers
1
Created
Source

fluidstate-preact

View interactive documentation on the official website.:

  • Preact Reactive Layer
  • Preact Bindings

fluidstate-preact provides Preact bindings for the fluidstate fine-grained reactivity library. It allows you to build highly performant Preact applications where components update automatically and efficiently in response to state changes, without manual subscriptions or monolithic state updates.

A Library with Two Roles

fluidstate-preact is designed to serve two distinct but complementary purposes:

  • Standalone Reactive Layer: It offers a reactive layer for fluidstate built on top of @preact/signals-core. This part of the library can be used independently of Preact, enabling you to leverage the performance of Preact Signals in any fluidstate-powered JavaScript application.

  • Preact Bindings: It provides a rich set of tools—hooks, Higher-Order Components (HOCs), and signal-based utilities—to seamlessly integrate fluidstate's reactive state into your Preact components, offering multiple patterns for different use cases.

Features

  • Fine-grained reactivity: Components re-render only when the specific data they use changes.
  • Multiple Integration Patterns: Choose from the useReactive hook, withReactive HOC, or the hyper-performant useSignals hook.
  • Standalone Reactive Layer: A fluidstate reactive layer powered by @preact/signals-core that can be used in any environment, even without Preact.
  • createReactiveSetup: A powerful pattern for creating encapsulated, reusable, and testable state management modules with Providers and consumer hooks/HOCs.
  • Seamless integration: Works with the entire fluidstate ecosystem, regardless of the underlying reactive layer.

Installation

You will need fluidstate and fluidstate-preact. Depending on which features you use, you may also need to install preact, @preact/signals-core, and @preact/signals as peer dependencies.

# Core library
npm install fluidstate fluidstate-preact

# or
yarn add fluidstate fluidstate-preact

Optional Peer Dependencies:

  • To use the Preact bindings (useReactive, withReactive, createReactiveSetup): install preact.
  • To use the useSignals hook: install preact and @preact/signals.
  • To use the standalone reactive layer: install @preact/signals-core.

Example: To use everything, you would install:

npm install preact @preact/signals @preact/signals-core
# or
yarn add preact @preact/signals @preact/signals-core

Part 1: Standalone Reactive Layer (via @preact/signals)

fluidstate-preact includes a reactive layer that bridges fluidstate with @preact/signals-core. This allows you to use fluidstate's ergonomic API while leveraging the highly efficient Preact Signals engine under the hood.

This layer can be used in any project, even one that doesn't use Preact for its UI, making it a powerful choice for any fluidstate-based application.

Setup

To enable fluidstate, you must provide it with a reactive layer. This is a one-time setup at your application's entry point.

// file: main.ts
import {
	provideReactiveLayer,
	createReactive,
	createReaction,
	runAction,
} from "fluidstate";
// Note the import path for the reactive layer
import { getReactiveLayer } from "fluidstate-preact/reactive-layer";

// 1. Get the reactive layer from fluidstate-preact
const reactiveLayer = getReactiveLayer();

// 2. Provide it to fluidstate
provideReactiveLayer(reactiveLayer);

// 3. You can now use the fluidstate API throughout your application
const counter = createReactive({ value: 0 });

createReaction(() => {
	console.log(`Counter value: ${counter.value}`);
});
// LOGS: Counter value: 0

runAction(() => {
	counter.value++;
});
// LOGS: Counter value: 1

After this setup, you can use the fluidstate package for all your state management tasks. For more details on createReactive, createReaction, and other features, please refer to the main fluidstate documentation.

Part 2: Preact Bindings

This part of the library is focused on integrating your fluidstate reactive state into Preact components.

One-Time Setup

Before using any Preact bindings, you must configure fluidstate with a reactive layer, as shown in Part 1. You can use any fluidstate reactive layer (e.g., fluidstate-alien, fluidstate-mobx or fluidstate-preact/reactive-layer). The Preact bindings are agnostic to the underlying engine.

Three Ways to Consume State

fluidstate-preact offers three primary ways to make your components reactive, each with different trade-offs.

1. useReactive Hook

The useReactive hook is a flexible way to consume reactive state. You provide a function that reads from your state, and the hook ensures your component re-renders whenever any of the accessed state properties change.

Example Store:

// file: counter-store.ts
import { createReactive } from "fluidstate";

export type CounterStore = {
	count: number;
	increment: () => void;
	decrement: () => void;
};

export const counterStore = createReactive<CounterStore>({
	count: 0,
	increment() {
		counterStore.count++;
	},
	decrement() {
		counterStore.count--;
	},
});

Component using useReactive:

// file: counter.tsx
import { useReactive } from "fluidstate-preact";
import { counterStore } from "./counter-store";

export const Counter = () => {
	// The component will re-render only when `counterStore.count` changes.
	const count = useReactive(() => counterStore.count);

	return (
		<div>
			<h1>Counter: {count}</h1>
			<button onClick={counterStore.increment}>Increment</button>
			<button onClick={counterStore.decrement}>Decrement</button>
		</div>
	);
};

2. withReactive HOC

The withReactive HOC provides an alternative, wrapping your component to make it automatically re-render when any reactive state it accesses changes.

// file: counter-hoc.tsx
import { withReactive } from "fluidstate-preact";
import { counterStore } from "./counter-store";

export const CounterComponent = withReactive(() => {
	return (
		<div>
			<h1>Counter: {counterStore.count}</h1>
			<button onClick={counterStore.increment}>Increment</button>
			<button onClick={counterStore.decrement}>Decrement</button>
		</div>
	);
});

3. useSignals Hook

The useSignals hook is the most performant option. It bridges fluidstate state with @preact/signals, allowing you to update parts of your UI without re-rendering the entire component. This requires @preact/signals to be installed.

You can import useSignals from fluidstate-preact/signals.

// file: counter-signals.tsx
import { useSignals } from "fluidstate-preact/signals";
import { counterStore } from "./counter-store";

export const Counter = () => {
	// `get` is a function that creates a Preact signal for a given piece of state.
	const get = useSignals();

	return (
		<div>
			{/* The component itself only renders once. Only the text nodes update. */}
			<h1>Counter: {get(() => counterStore.count)}</h1>
			<button onClick={counterStore.increment}>Increment</button>
			<button onClick={counterStore.decrement}>Decrement</button>
		</div>
	);
};

For larger applications, createReactiveSetup helps create encapsulated, testable, and reusable state modules. It generates a Provider, consumer hooks/HOCs, and a Context object for a specific slice of state.

Any reactions (e.g., for side effects) returned in a reactions array will be automatically cleaned up when the Provider unmounts.

Example: User Profile Module

1. Create the reactive setup

// file: user-profile-setup.ts
import {
	createReactive,
	Reaction,
	createReaction,
	cloneInert,
} from "fluidstate";
import { createReactiveSetup } from "fluidstate-preact";

export type UserProfileSetupProps = { initialName: string };
export type UserProfileData = { name: string; email: null | string };
export type UserProfileActions = {
	updateName: (newName: string) => void;
	setEmail: (email: string) => void;
};
export type UserProfileState = {
	data: UserProfileData;
	actions: UserProfileActions;
	reactions: Reaction[];
};

// Generate and rename the setup utilities for clarity.
export const {
	ReactiveProvider: UserProfileProvider,
	useReactiveState: useUserProfileState,
	withReactiveState: withUserProfileState,
	MockProvider: MockUserProfileProvider,
	StateContext: UserProfileContext, // Export the context for use with `useSignals`
} = createReactiveSetup((props: UserProfileSetupProps): UserProfileState => {
	const data = createReactive<UserProfileData>({
		name: props.initialName,
		email: null,
	});
	const actions = createReactive<UserProfileActions>({
		updateName(newName: string) {
			data.name = newName;
		},
		setEmail(email: string) {
			data.email = email;
		},
	});
	const syncProfile = createReaction(() => {
		fetch(`api/syncProfile`, {
			method: "POST",
			body: JSON.stringify(cloneInert(data)),
		});
	});
	return { data, actions, reactions: [syncProfile] };
});

2. Provide the state

// file: app.tsx
import { UserProfileProvider } from "./user-profile-setup";
import { UserProfileEditor } from "./user-profile-editor";
import { UserProfileDisplay } from "./user-profile-display";

export const App = () => (
	<UserProfileProvider setupProps={{ initialName: "John Doe" }}>
		<div>
			<h1>User Management</h1>
			<UserProfileEditor />
			<hr />
			<UserProfileDisplay />
		</div>
	</UserProfileProvider>
);

3. Consume the state

You can now consume state using the hook, HOC, or useSignals.

Using the useReactiveState hook:

// file: user-profile-editor.tsx
import { useUserProfileState } from "./user-profile-setup";

export const UserProfileEditor = () => {
	const { name, email, updateName, setEmail } = useUserProfileState(
		(state) => ({
			name: state.data.name,
			email: state.data.email,
			updateName: state.actions.updateName,
			setEmail: state.actions.setEmail,
		})
	);
	// ... component logic
};

Using useSignals for maximum performance:

// file: user-profile-display.tsx
import { useSignals } from "fluidstate-preact/signals";
import { UserProfileContext } from "./user-profile-setup";

export const UserProfileDisplay = () => {
	// Pass the context to `useSignals` to connect to the provider state.
	const get = useSignals(UserProfileContext);
	return (
		<div>
			<h2>Current Profile</h2>
			<p>
				<strong>Name:</strong> {get((state) => state.data.name)}
			</p>
			<p>
				<strong>Email:</strong> {get((state) => state.data.email ?? "Not set")}
			</p>
		</div>
	);
};

API Reference

useReactive<T>(getState: () => T, dependencyArray?: ReadonlyArray<unknown>): T

A Preact hook that subscribes a component to reactive state changes, causing a re-render when dependencies change.

withReactive<P>(Component: FunctionComponent<P>): FunctionComponent<P>

A HOC that makes a functional component reactive. It will re-render whenever any reactive state it accesses changes.

useSignals<CV>(Context?: Context<CV>): (fn: (value: CV) => T) => Signal<T>

A hook that returns a function to create Preact signals from fluidstate data.

  • Context: Optional. A Preact Context object (like one from createReactiveSetup) to source the state from. If omitted, it works with the state passed from the component.
  • The returned function get(fn) takes a selector and returns a memoized @preact/signal signal for the selected value.

createReactiveSetup<SetupProps, State>(createState)

A factory that creates a set of utilities for encapsulated state management.

It returns a ReactiveSetup object with:

  • ReactiveProvider: A Provider component that accepts setupProps.
  • useReactiveState: A hook to consume the state.
  • withReactiveState: A HOC to inject the state via a state prop.
  • StateContext: The Preact Context object used by the Provider. Pass this to useSignals to connect to the provided state.
  • MockProvider: A provider for testing that accepts a value prop with mock state.
  • createState: The original createState function, which can be useful for testing the state logic itself.

Package Entry Points

To keep the library modular and dependencies optional, features are exposed via different entry points:

  • fluidstate-preact: Core Preact bindings.

    • useReactive
    • withReactive
    • createReactiveSetup
  • fluidstate-preact/signals: The useSignals hook that uses @preact/signals.

    • useSignals
  • fluidstate-preact/reactive-layer: The standalone reactive layer backed by @preact/signals-core.

    • getReactiveLayer

Keywords

state

FAQs

Package last updated on 19 Jun 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.