- 🎯 Type-Safe: Full TypeScript support with zero configuration
- 🚀 Simple API: Intuitive hooks-based API
- ⚡ Lightweight: ~4KB minified + gzipped
- 💾 Built-in Persistence: Optional localStorage persistence
- 🔄 Middleware System: Extensible with custom middleware
- 🛠️ Developer Tools: Time-travel debugging (coming soon)
npm install nextjs-state
# or
yarn add nextjs-state
# or
pnpm add nextjs-state
// state/index.ts
import { createNextState } from 'nextjs-state';
interface AppState {
count: number;
lastUpdated: string;
}
export const { Provider, useNextState } = createNextState<AppState>({
initialState: {
count: 0,
lastUpdated: new Date().toISOString(),
},
});
// app/layout.tsx
import { Provider } from './state';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<Provider>{children}</Provider>
</body>
</html>
);
}
// app/components/Counter.tsx
import { useNextState } from '../state';
export function Counter() {
const { state, setState } = useNextState((state) => ({
count: state.count,
lastUpdated: state.lastUpdated,
}));
const increment = () => {
setState({
count: state.count + 1,
lastUpdated: new Date().toISOString(),
});
};
return (
<div>
<p>Count: {state.count}</p>
<p>Last Updated: {new Date(state.lastUpdated).toLocaleString()}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
nextjs-state uses a simple and intuitive state management pattern:
- Define your state with TypeScript interfaces
- Create a state instance with
createNextState
- Wrap your app with the
Provider
- Use
useNextState
hook to access and update state
interface User {
id: string;
name: string;
email: string;
}
interface AppState {
user: User | null;
theme: 'light' | 'dark';
}
// Type errors caught at compile time
const wrongUpdate = setState({
user: {
id: 123, // Error: Type 'number' is not assignable to type 'string'
name: 'John', // Error: Missing property 'email'
},
});
// Simple update
setState({ count: count + 1 });
// Update with previous state
setState((prev) => ({
count: prev.count + 1,
}));
// Async update
const fetchUser = async (userId: string) => {
const user = await api.getUser(userId);
setState({ user });
};
const loggerMiddleware = {
id: 'logger',
onStateChange: (prev, next) => {
console.log('State updated:', { prev, next });
},
onError: (error) => {
console.error('State error:', error);
},
};
const { Provider } = createNextState({
initialState,
options: {
middleware: [loggerMiddleware],
},
});
State is automatically persisted to localStorage by default. You can access the storage API directly:
const { Provider, useNextState } = createNextState({
initialState: {
count: 0,
},
});
// State will be automatically persisted to localStorage
Creates a new state instance with the provided configuration.
const { Provider, useNextState } = createNextState<AppState>({
initialState: {
// Your initial state
},
options?: {
middleware?: NextStateMiddleware<T>[];
},
});
Hook to access and update state.
const { state, setState } = useNextState((state) => ({
// Select state properties
}));
Interface for creating custom middleware.
interface NextStateMiddleware<T> {
id?: string;
priority?: number;
onStateChange?: (prev: T, next: T) => void | Promise<void>;
onError?: (error: Error) => void;
}
We welcome contributions! Please see our Contributing Guide for details.
MIT © [Rahees Ahmed]