Admin Panel API: Redux store & reducers
Page summary:
Use
addReducers()duringregisterto add custom state to the Redux store. Then read state withuseSelector, update it withuseDispatch, and subscribe to changes withuseStore. Theadmin_appslice exposes theme, locale, permissions, and authentication data.
Strapi's admin panel uses a global Redux store to manage application state. Plugins can access this store to read state, dispatch actions, and subscribe to state changes. This enables plugins to interact with core admin functionality like theme settings, language preferences, and authentication state.
Before diving deeper into the concepts on this page, please ensure you have:
- created a Strapi plugin,
- read and understood the basics of the Admin Panel API
Store overview
The Redux store is automatically provided to all plugin components through React Redux's Provider. The store contains several slices:
admin_app: core admin state including theme, language, permissions, and authentication tokenadminApi: RTK Query API state for admin endpoints- Plugin-specific slices: additional reducers added by plugins
Adding custom reducers
Reducers are Redux reducers that can be used to share state between components. Reducers can be useful when:
- Large amounts of application state are needed in many places in the application.
- The application state is updated frequently.
- The logic to update that state may be complex.
Reducers can be added to a plugin interface with the addReducers() function during the register lifecycle.
A reducer is declared as an object with this syntax:
- JavaScript
- TypeScript
import { exampleReducer } from './reducers'
import pluginId from './pluginId'
const reducers = {
// Reducer Syntax
[`${pluginId}_exampleReducer`]: exampleReducer
}
export default {
register(app) {
app.addReducers(reducers)
},
bootstrap() {},
};
import type { StrapiApp } from '@strapi/admin/strapi-admin';
import { exampleReducer } from './reducers';
import pluginId from './pluginId';
const reducers = {
[`${pluginId}_exampleReducer`]: exampleReducer,
};
export default {
register(app: StrapiApp) {
app.addReducers(reducers);
},
bootstrap() {},
};
Reading state with useSelector
The most common way to access Redux state in your plugin components is using the useSelector hook from react-redux:
- JavaScript
- TypeScript
import { useSelector } from 'react-redux';
const HomePage = () => {
// Read current theme
const currentTheme = useSelector(
(state) => state.admin_app?.theme?.currentTheme
);
// Read current locale
const currentLocale = useSelector(
(state) => state.admin_app?.language?.locale
);
// Read authentication status
const isAuthenticated = useSelector((state) => !!state.admin_app?.token);
// Read available locales
const availableLocales = useSelector(
(state) => state.admin_app?.language?.localeNames || {}
);
return (
<div>
<p>Current Theme: {currentTheme}</p>
<p>Current Locale: {currentLocale}</p>
<p>Authenticated: {isAuthenticated ? 'Yes' : 'No'}</p>
</div>
);
};
import { useSelector } from 'react-redux';
const HomePage = () => {
// Read current theme
const currentTheme = useSelector(
(state: any) => state.admin_app?.theme?.currentTheme
);
// Read current locale
const currentLocale = useSelector(
(state: any) => state.admin_app?.language?.locale
);
// Read authentication status
const isAuthenticated = useSelector((state: any) => !!state.admin_app?.token);
// Read available locales
const availableLocales = useSelector(
(state: any) => state.admin_app?.language?.localeNames || {}
);
return (
<div>
<p>Current Theme: {currentTheme}</p>
<p>Current Locale: {currentLocale}</p>
<p>Authenticated: {isAuthenticated ? 'Yes' : 'No'}</p>
</div>
);
};
Available state properties
The admin_app slice contains the following state properties:
| Property | Type | Description |
|---|---|---|
theme.currentTheme | string | Current theme ('light', 'dark', or 'system') |
theme.availableThemes | string[] | Array of available theme names |
language.locale | string | Current locale code (e.g., 'en', 'fr') |
language.localeNames | object | Object mapping locale codes to display names |
token | string | null | Authentication token |
permissions | object | User permissions object |
Dispatching actions
To update the Redux store, use the useDispatch hook:
The examples below dispatch actions to core admin state (theme, locale) for illustration purposes. In practice, most plugins should dispatch actions to their own custom reducers rather than modifying global admin state.
- JavaScript
- TypeScript
import { useSelector, useDispatch } from 'react-redux';
const HomePage = () => {
const dispatch = useDispatch();
const currentTheme = useSelector(
(state) => state.admin_app?.theme?.currentTheme
);
const handleToggleTheme = () => {
const newTheme =
currentTheme === 'light'
? 'dark'
: currentTheme === 'dark'
? 'system'
: 'light';
dispatch({
type: 'admin/setAppTheme',
payload: newTheme,
});
};
const handleChangeLocale = (locale) => {
dispatch({
type: 'admin/setLocale',
payload: locale,
});
};
return (
<div>
<button onClick={handleToggleTheme}>
Toggle Theme (Current: {currentTheme})
</button>
<button onClick={() => handleChangeLocale('en')}>Set English</button>
</div>
);
};
import { useSelector, useDispatch } from 'react-redux';
const HomePage = () => {
const dispatch = useDispatch();
const currentTheme = useSelector(
(state: any) => state.admin_app?.theme?.currentTheme
);
const handleToggleTheme = () => {
const newTheme =
currentTheme === 'light'
? 'dark'
: currentTheme === 'dark'
? 'system'
: 'light';
dispatch({
type: 'admin/setAppTheme',
payload: newTheme,
} as any);
};
const handleChangeLocale = (locale: string) => {
dispatch({
type: 'admin/setLocale',
payload: locale,
} as any);
};
return (
<div>
<button onClick={handleToggleTheme}>
Toggle Theme (Current: {currentTheme})
</button>
<button onClick={() => handleChangeLocale('en')}>Set English</button>
</div>
);
};
Available actions
The admin_app slice provides the following actions:
| Action type | Payload type | Description |
|---|---|---|
admin/setAppTheme | string | Set the theme ('light', 'dark', or 'system') |
admin/setAvailableThemes | string[] | Updates theme.availableThemes in admin_app |
admin/setLocale | string | Set the locale (e.g., 'en', 'fr') |
admin/setToken | string | null | Set the authentication token |
admin/login | { token: string, persist?: boolean } | Login action with token and persistence option |
admin/logout | void | Logout action (no payload) |
When dispatching actions, use the Redux Toolkit action type format: 'sliceName/actionName'. The admin slice is named 'admin', so actions follow the pattern 'admin/actionName'.
Accessing the store instance
For advanced use cases, you can access the store instance directly using the useStore hook:
- JavaScript
- TypeScript
import { useStore } from 'react-redux';
import { useEffect } from 'react';
const App = () => {
const store = useStore();
useEffect(() => {
const state = store.getState();
console.log('Redux Store State:', state);
const unsubscribe = store.subscribe(() => {
const currentState = store.getState();
console.log('Store state changed:', {
theme: currentState.admin_app?.theme?.currentTheme,
locale: currentState.admin_app?.language?.locale,
timestamp: new Date().toISOString(),
});
});
return () => {
unsubscribe();
};
}, [store]);
return <div>My Plugin</div>;
};
import { useStore } from 'react-redux';
import { useEffect } from 'react';
const App = () => {
const store = useStore();
useEffect(() => {
const state = store.getState();
console.log('Redux Store State:', state);
const unsubscribe = store.subscribe(() => {
const currentState = store.getState();
console.log('Store state changed:', {
theme: currentState.admin_app?.theme?.currentTheme,
locale: currentState.admin_app?.language?.locale,
timestamp: new Date().toISOString(),
});
});
return () => {
unsubscribe();
};
}, [store]);
return <div>My Plugin</div>;
};
Complete example
The following example combines all 3 patterns (useSelector, useDispatch, useStore) described on the present page:
- JavaScript
- TypeScript
Best practices
- Use
useSelectorfor reading state. PreferuseSelectorover direct store access. It automatically subscribes to updates and re-renders components when the selected state changes. - Clean up subscriptions. Always unsubscribe from store subscriptions in
useEffectcleanup functions to prevent memory leaks. - Consider type safety. For Redux state access in plugins, use
react-reduxhooks (useSelector,useDispatch) with plugin-local typing (for exampleRootStateandAppDispatch). If you use Strapi admin utilities, import them from@strapi/admin/strapi-admin(not@strapi/admin). Avoid relying on undocumented typed Redux hooks as part of Strapi's public API until they are explicitly documented as stable. - Avoid unnecessary dispatches. Only dispatch actions when you need to update state. Reading state does not require dispatching actions.
- Respect core state. Be careful when modifying core admin state (like theme or locale) as it affects the entire admin panel. Consider whether your plugin should modify global state or maintain its own local state.
To add your own state to the Redux store, see Adding custom reducers above.