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 (or dark if you set localStorage).
  • 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.