Goal
Create a ui slice for theme state, build the Redux store with configureStore, and wrap the app in <Provider>.
Navigation between Jobs and Saved will use TanStack Router later , keep this slice focused on theme only.
1. Why a slice?
A slice is one feature of your store. Instead of writing action type strings and a switch statement by hand, createSlice generates:
- the reducer
- action creators (
setTheme,toggleTheme, …) - action types under the hood
2. Create src/store/uiSlice.js
import { createSlice } from "@reduxjs/toolkit";
const saved = localStorage.getItem("hireboard-theme");
const initialTheme = saved === "dark" || saved === "light" ? saved : "light";
const uiSlice = createSlice({
name: "ui",
initialState: {
theme: initialTheme,
},
reducers: {
setTheme(state, action) {
state.theme = action.payload;
localStorage.setItem("hireboard-theme", action.payload);
},
toggleTheme(state) {
state.theme = state.theme === "dark" ? "light" : "dark";
localStorage.setItem("hireboard-theme", state.theme);
},
},
});
export const { setTheme, toggleTheme } = uiSlice.actions;
export default uiSlice.reducer;
Chunks explained
| Chunk | What it does |
|---|---|
createSlice({ ... }) |
Builds reducer + actions for the ui feature |
name: "ui" |
Prefixes action types (ui/setTheme) |
initialState.theme |
"light" or "dark" |
state.theme = ... |
Looks like a mutation , Immer makes a safe copy |
action.payload |
The value you pass to dispatch(setTheme("dark")) |
localStorage |
Remembers theme across refreshes |
3. Create src/store/index.js
import { configureStore } from "@reduxjs/toolkit";
import uiReducer from "./uiSlice";
export const store = configureStore({
reducer: {
ui: uiReducer,
},
});
Chunks explained
| Chunk | What it does |
|---|---|
configureStore |
Creates the store with DevTools + default middleware |
reducer: { ui: uiReducer } |
Mounts the ui slice at state.ui |
export const store |
One store for the whole app |
After this, global state looks like:
{
ui: {
theme: "light",
},
}
4. Provide the store in src/main.jsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { Provider } from "react-redux";
import { store } from "./store";
import App from "./App.jsx";
import "./style.css";
createRoot(document.getElementById("root")).render(
<StrictMode>
<Provider store={store}>
<App />
</Provider>
</StrictMode>,
);
Chunks explained
| Chunk | What it does |
|---|---|
Provider |
Makes the store available to every child |
store={store} |
The store you just configured |
Wrap outside <App /> |
So all components can use Redux hooks |
If you forget <Provider>, hooks throw: "could not find react-redux context value".
5. Apply the initial theme on load
In App.jsx:
import { useEffect } from "react";
import { useSelector } from "react-redux";
function App() {
const theme = useSelector((state) => state.ui.theme);
useEffect(() => {
document.documentElement.classList.toggle("dark", theme === "dark");
}, [theme]);
return (
<div className="min-h-screen bg-surface font-sans text-ink antialiased dark:bg-slate-950 dark:text-slate-100">
<header className="border-b border-slate-200 bg-card px-6 py-4 dark:border-slate-800 dark:bg-slate-900">
<h1 className="text-xl font-semibold text-brand-600">HireBoard</h1>
<p className="text-sm text-muted dark:text-slate-400">
Theme in store:{" "}
<span className="font-medium text-ink dark:text-white">{theme}</span>
</p>
</header>
<main className="mx-auto max-w-5xl px-6 py-10">
<p className="text-muted dark:text-slate-400">
Store is live. Next: toggle theme with <code>useDispatch</code>.
</p>
</main>
</div>
);
}
export default App;
Chunks explained
| Chunk | What it does |
|---|---|
useSelector((state) => state.ui.theme) |
Subscribes to theme; re-renders when it changes |
useEffect + classList.toggle |
Keeps Tailwind dark: utilities in sync |
Optional: Redux DevTools
Install the Redux DevTools browser extension. With configureStore, time-travel debugging works out of the box.
Check
- App loads without Provider errors.
- Header shows
Theme in store: light(ordarkif you setlocalStorage). - In Redux DevTools you see
{ ui: { theme } }.
Assessment: Taskly : Store and slice
In the taskly app: create uiSlice with theme, configureStore, and wrap with <Provider>. Persist theme under taskly-theme.
Done when: Redux DevTools shows { ui: { theme } } in Taskly.
Next: build ThemeToggle and a Navbar shell that dispatch real actions.