DEV Community

Cover image for How To Theme Your App With React, Redux & SCSS
Ebuka
Ebuka

Posted on

How To Theme Your App With React, Redux & SCSS

An opinionated guide on how to theme your react application with SCSS and Redux.

Steps to follow

Create a new app with create-react-app

npx create-react-app myapp
Enter fullscreen mode Exit fullscreen mode

Add structure to your app:

...
src
  store
    theme
      theme.slice.js
      theme.actions.js
    rootReducer
  styles
    _themes.scss
    _themify.scss
  components
    layout
      layout.jsx
      layout.scss
  page
    home
      home.jsx
    about
      about.jsx
  App.js
...
Enter fullscreen mode Exit fullscreen mode

Add these packages to the app

npm i @reduxjs/toolkit redux node-sass react-router-dom react-redux --save
Enter fullscreen mode Exit fullscreen mode

Create the theme slice

// theme.slice.js
import { createSlice } from '@reduxjs/toolkit'
import { uiActions } from './ui.actions'

export const uiSlice = createSlice({
  name: 'ui',
  initialState: 'light-theme',
  reducers: uiActions
})

export const { toggleTheme } = uiSlice.actions

export default uiSlice.reducer
Enter fullscreen mode Exit fullscreen mode

Create the theme action

// theme.action.js
export const uiActions = {
  toggleTheme: state => {
    console.log(state);
    return state === 'dark-theme' ? 'light-theme' : 'dark-theme';
  }
};
Enter fullscreen mode Exit fullscreen mode

Create the reducer

// rootReducer.js
import { configureStore } from '@reduxjs/toolkit'
import uiReducer from './ui/ui.slice'

export default configureStore({
  reducer: {
    ui: uiReducer,
  }
})
Enter fullscreen mode Exit fullscreen mode

Create your theme map

// themes.scss
$themes: (
  light: (
    bg: #f5fcff,
    fg: #002433,
  ),
  dark: (
    bg: #004966,
    fg: #f5fcff,
  ),
);
Enter fullscreen mode Exit fullscreen mode

Create the themify @mixin

// themify.scss
@import 'themes';

@mixin themify($property, $key, $themes: $themes) {
  // Iterate over the themes
  @each $theme, $colors in $themes {
    // Create a selector
    &.#{$theme}-theme,
    .#{$theme}-theme & {
      // Output the declaration
      #{$property}: map-get($colors, $key);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Create the layout component

// layout.jsx
import { useSelector, useDispatch } from "react-redux";
import { toggleTheme } from "../../store/ui/ui.slice";
import './layout.scss';

export default function Layout(props) {

  const theme = useSelector(state => state.ui);
  const dispatch = useDispatch();

  return (
    <>
      <header className={`${theme} container`}>
        <Header />
        <button onClick={() => dispatch(toggleTheme())}>Toggle Theme</button>
      </header>

      <div className={`${theme} container`}>
        {props.children}
      </div>
    </>
  )
}
Enter fullscreen mode Exit fullscreen mode

Apply styles to the layout

// layout.scss
@import "../../assets/style/themify";

header {
  @include themify('background', 'body-bg');
}
Enter fullscreen mode Exit fullscreen mode

Now create your pages

// home.jsx
import { Layout } from "../../components/layout/layout";

export default function Home() {
  return (
    <Layout>
      <section>
        Home Page
      </section>
    </Layout>
  )
}
Enter fullscreen mode Exit fullscreen mode
// about.jsx
import { Layout } from "../../components/layout/layout";

export default function About() {
  return (
    <Layout>
      <section>
        About Page
      </section>
    </Layout>
  )
}
Enter fullscreen mode Exit fullscreen mode

Create routes for the application

// App.js
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Home from './pages/home/home';
import About from './pages/about/about';

function App() {

  return (
    <Router>
      <Switch>
        <Route exact path='/' component={Home} />
        <Route exact path='/tasks' component={About} />
      </Switch>
    </Router>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

The output:
Run the output on codepen.

Thanks.

Top comments (0)