A collection of React hooks and SSR utilities for KarpelesLab Framework.
npm install @karpeleslab/react-klbfw-hooks
- Shared state across components through named variables
- SSR (Server-Side Rendering) with React
- REST API integration with caching
- React Router v7 support for modern routing with SSR
- Promise handling for data loading during SSR
- useRest(path, params, noThrow, cacheLifeTime): Fetches data from your backend with automatic caching and SSR support
- useVar(varName, defaultValue): Provides shared state accessible by name throughout your application
- useVarSetter(varName, defaultValue): Returns only a setter for the given variable without subscribing to updates
- usePromise(promise): Registers a promise for SSR to wait for before rendering
- useRestRefresh(path, params, cacheLifeTime): Returns only the refresh function for a REST endpoint
- useRestResetter(): Returns a function to clear all REST cache (useful for logout)
The entry point function that replaces ReactDOM.render/hydrate with SSR support. Takes a route configuration and optional settings.
Parameters:
routes
: A React Router route configuration created withcreateRoutesFromElements
or directly as a route objectpromisesOrOptions
: Either an array of promises to wait for, or an options objectoptions
: Configuration options (when using promises as the second parameter)
Options object properties:
routerProps
: Additional props to pass to the Router component (useful for injecting stores or providers)contextProps
: Additional props to pass to the internal Context.Provider
import { run } from "@karpeleslab/react-klbfw-hooks";
import { Route, createRoutesFromElements } from "react-router-dom";
import Home from './routes/Home';
// Create routes with the Data Router API
const routes = createRoutesFromElements(
<Route path="/" element={<Home />} />
);
// Pass the routes to run
run(routes);
import { run } from "@karpeleslab/react-klbfw-hooks";
import { Route, createRoutesFromElements } from "react-router-dom";
import { Provider } from 'react-redux';
import { store } from './store';
import Home from './routes/Home';
// Create routes
const routes = createRoutesFromElements(
<Route path="/" element={<Home />} />
);
// Inject the Redux store by providing custom router props
run(routes, {
routerProps: {
// These props will be passed to the RouterProvider (client) or StaticRouterProvider (server)
fallbackElement: <div>Loading...</div>,
future: {
v7_startTransition: true
}
},
contextProps: {
// You can also wrap the entire app with providers using React.createElement in your index.js
// This is an alternative approach for global store/provider setup
}
});
import { run } from "@karpeleslab/react-klbfw-hooks";
import { redirect, Route, createRoutesFromElements } from "react-router-dom";
import Home from './routes/Home';
import About from './routes/About';
import Contact from './routes/Contact';
// Create routes
const routes = createRoutesFromElements(
<>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route
path="/contact"
element={<Contact />}
loader={async () => {
// Load data needed for this route
const data = await fetch('/api/contact-info').then(r => r.json());
return data;
}}
/>
<Route
path="/redirect"
loader={() => {
// Redirect example with status code
return redirect("/about", 301);
}}
/>
</>
);
// Pass routes to run
run(routes);
The library provides full support for React Router v7 SSR features including:
- Data Loading: Routes with loaders will have their data pre-loaded during SSR
- Redirects: Redirect responses from loaders are properly handled with status codes preserved
- Data Router API: Uses the modern React Router v7 data APIs (createBrowserRouter, createStaticHandler, createStaticRouter, RouterProvider, StaticRouterProvider)
Under the hood, the implementation:
- On the client: Creates a browser router from routes using
createBrowserRouter
- On the server: Creates a static handler from routes using
createStaticHandler
- Processes routes with
query
function to detect redirects and load data - Renders using
createStaticRouter
andStaticRouterProvider
for SSR - Preserves HTTP status codes (301, 302, etc.) for proper SEO
This allows you to use all modern React Router features while still benefiting from server-side rendering.
With i18n support:
import { run } from "@karpeleslab/react-klbfw-hooks";
import { Route } from "react-router-dom";
import Home from './routes/Home';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import { Backend } from '@karpeleslab/i18next-klb-backend';
import { getLocale } from "@karpeleslab/klbfw";
let i18nOpt = {
lng: getLocale(),
initImmediate: false,
load: 'currentOnly',
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
react: {
useSuspense: false,
}
};
// Define your routes as an array of <Route> elements
const routes = [
<Route path="/" element={<Home />} key="home" />
];
run(routes, [i18n.use(Backend).use(initReactI18next).init(i18nOpt)]);
Hook for creating/accessing named variables which share a value anywhere in the application.
function Counter() {
const [count, setCount] = useVar("counter", 0);
return (
<div>
Count is {count}
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
In another component:
function DisplayCounter() {
const [count] = useVar("counter", 0);
return <div>Current count: {count}</div>;
}
Returns only a setter for the given variable, without subscribing the current component to variable updates.
function CounterControl() {
const setCount = useVarSetter("counter", 0);
return <button onClick={() => setCount(0)}>Reset Counter</button>;
}
Registers a promise that the server needs to wait for in SSR before rendering. Useful for ensuring data is available during server-side rendering.
function DataComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const promise = fetchData().then(result => setData(result));
usePromise(promise);
}, []);
return <div>{data ? JSON.stringify(data) : "Loading..."}</div>;
}
Performs a REST GET request to the specified path, caching the result and returning it in a way that is safe for rendering.
function UserProfile({ userId }) {
const [user, refreshUser] = useRest(`/api/users/${userId}`);
return (
<div>
{user ? (
<>
<h2>{user.name}</h2>
<p>{user.email}</p>
<button onClick={refreshUser}>Refresh</button>
</>
) : "Loading..."}
</div>
);
}
With parameters:
function SearchResults() {
const [query, setQuery] = useState("");
const [results, refreshResults] = useRest("/api/search", { q: query });
return (
<div>
<input
value={query}
onChange={e => setQuery(e.target.value)}
/>
<button onClick={refreshResults}>Search</button>
{results && results.map(item => (
<div key={item.id}>{item.title}</div>
))}
</div>
);
}
MIT