Skip to content
banner

Building a React app with TypeScript and Material Design

React, Material Design11 min read

"Building a React app with TypeScript and Material Design"

Hi there, Developers 🖖 Welcome to the last post of the year! Phew, what a year, huh? Apart from all the negative impacts of the COVID in the entire world, I wish you could have had time-off from your crazy busy work/study routine, have had the time to realize what your true values are, and most importantly, to appreciate the real and simple things in life!

As announced a couple of weeks ago, the Welcome, Developer team has released our first project 🎉 It's a React boilerplate app built using TypeScript and Material Design!

The goal is to provide us with a good start point in future posts' exercises, and mainly, to encourage you to use it for learning purposes and/or for when starting your own React applications!

Today's plan for us is to go through the application together! Some steps might be pretty straightforward to follow or even sound repetitive for some of you, but hopefully, it'll be useful for people in the future just starting out.

Okey-dokey, shall we start then? As I always say, grab your favorite drink (no judgment here 🙂), and let's get our hands dirty!

Introduction

On this project, React is our choice when it comes to a JavaScript library! I'm a big fan of it, I have to say. Although, the decision of what library to use and what subjects to discuss here at our blog, is never about my personal preferences but always about trends and popularity.

React is an awesome library to work with! It enables you to very quickly find yourself already building components and highly motivated to explore! The official documentation is excellent and has everything you need to start developing your apps!

When coding a React app, we can choose between using JavaScript and/or TypeScript. If you wanna hear from my personal experience, I used to only use JavaScript with my React apps. However, since I started using TypeScript, there is no turning back! Pay attention to this description of TypeScript:

"TypeScript is a programming language developed by Microsoft. It is a typed superset of JavaScript, and includes its own compiler. Being a typed language, TypeScript can catch errors and bugs at build time, long before your app goes live."

-- React Documentation

It does sound very convincing, don't you think? Catching bugs before the app goes live? Sold, I'm in!

Honestly, it helps quite a lot with those runtime errors when a variable hasn't been declared, or the assigned value doesn't match with its type, and the list goes on! Based on those reasons, we can understand why the popularity of TypeScript has only increased in the last couple of years!

Alright, so we've got React and TypeScript so far. What about the Material Design?

Material is an open-source design system built by Google. It consists of a collection of guidelines and principles to design and create user interfaces (UI). You can find it when you access your Gmail, for example. It's pretty cool and enables us to build beautiful UIs! To have it applied to our React application, we're going to use the Material-UI component library. If you haven't heard about it before, check out the list of cool components available! Throughout the post, you will get yourself familiar with how to implement those Material components into the React app!

Enough talking, right? Let's get to work, developers 🔨

Environment Setup

In case you still don't have Node.js installed on your machine, download the current version from the official website, or using any other method of your preference such as nvm. Once it's been installed, run the npm command to ensure it's all good to start our exercise. You should be able to see the list of commands available.

Create React App

With the environment ready, let's start and create our React app. The easiest, and most recommended way, is using the create-react-app command:

1npx create-react-app welcomedev-react-starter --template typescript

Because we're using TypeScript on the application, we must add the typescript template to the creation command. It creates an additional file called tsconfig.json on the root of the application, which has the responsibility of specifying the root files and the compiler options required by the project.

Once the app has been created, navigate to its folder location and start it to ensure there are no errors (just in case!):

1cd welcomedev-react-starter
2yarn start

All good? React logo spinning? Let's move on then, guys ✅

This step is optional, so it's up to you to do it or not. I'm going to remove the files we won't be using:

"Delete Files"

In case you have also deleted the files highlighted above, do remember to remove the references to them in the App.tsx and index.tsx files, okay? Great, next step!

On the App.tsx file, let's clean it so we can start building our app:

src/App.tsx
1import React from 'react';
2
3function App() {
4 return (
5 <>
6 </>
7 );
8}
9
10export default App;

Cool, no distractions anymore. In the next section, we will start creating the React components of our SPA application!

TypeScript Components

If you have followed our previous posts on React, you will notice that the components we are creating here look a bit different from the others built using JavaScript. That's because the purpose now is to use TypeScript whenever possible, getting good benefits such as type-checking errors!

Let's start, developer. First of all, create a new folder called components under src. Then, create a TypeScript JSX file (.tsx) called Header:

src/components/Header.tsx
1import React, { ReactElement, FC } from "react";
2
3const Header: FC<any> = (): ReactElement => {
4 return (
5 <div>
6 {`Header`}
7 </div>
8 );
9};
10
11export default Header;

Good, we've just created a Functional Component (FC) that receives an object of any data type and returns a new React element (a div in the example).

Okay, got it. But it won't be much useful the type checking functionally of TypeScript if we use the type of any, do you agree? Time to start creating interfaces to represent objects! An interface is an approach used to create contracts within our code, so every part of the application (as well as us, the developers) knows exactly what properties and data types to expect!

What about we have a quick pause now, and have a read at interfaces on the TypeScript documentation? The concept can get quite complex but don't worry, only the basic level is explored in our app.

Ready to continue? Nice one. Back to the Header.tsx component, create the Props interface to represent the properties that the component receives as parameters:

src/components/Header.tsx
1import React, { ReactElement, FC } from "react";
2
3// define interface to represent component props
4interface Props {
5 title: String
6}
7
8const Header: FC<Props> = ({ title }): ReactElement => {
9 return (
10 <div>
11 {title}
12 </div>
13 );
14};
15
16export default Header;

Shall we check our TypeScript component in action? On the App.tsx file, add the reference to the recently created component:

src/App.tsx
1import React from 'react';
2
3import Header from './components/Header';
4
5function App() {
6 return (
7 <>
8 <Header title="React App" />
9 </>
10 );
11}
12
13export default App;

Start the development server, and check if the value of the title property is printed on the browser. Is it? Great, you have just learned the basic logic of every functional component we will be creating throughout the post 😎 Piece of cake, don't you think?

So far so good, my friends! Moving on to the next chapter!

Material Design UI

The goal is to build the user interfaces (UI) of our React application with the Material Design concepts applied to it. Therefore, we must install the Material-UI library:

1npm install @material-ui/core @material-ui/icons @types/material-ui

Notice above that it's been installed three libraries:

  1. @material-ui/core for the React components
  2. @material-ui/icons for the Material icons
  3. @types/material-ui for the type definitions of Material-UI

Last but not least, go to the index.html file in the public folder, and apply the links to fonts and icons used by the Material-UI library:

public/index.html
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5 <meta charset="utf-8" />
6 <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
7 <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
8 <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
9 <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
10 <meta name="theme-color" content="#000000" />
11 <meta name="description" content="Web site created using create-react-app" />
12 <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
13 <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
14 <title>Welcome, Developer</title>
15</head>
16
17<body>
18 <noscript>You need to enable JavaScript to run this app.</noscript>
19 <div id="root"></div>
20</body>
21
22</html>

Fantastic, we're ready to go! But first, we must build a few React components to get ourselves familiar with TypeScript and Material-UI. Remember, the best way to learn is through repetition 💪

For learning purposes, I truly recommend you to download (or fork, or clone) the official application to your local machine and spend as much time exploring it as you think it's necessary for you to learn concepts such as CSS-in-JS and how to integrate the Material-UI components.

Unfortunately, this post would be too extensive and verbose if I cover in detail every component built on the boilerplate app. However, should we pick one as an example and go over it? Awesome, let's do it!

Hold on, one simple thing I'd like to talk about briefly. When exploring the components yourself, you will notice references to a constant file. It's highly recommended to keep all the constant values in a single location, making it easier to change properties being used all along in the application. Below is the constant file being used in the app, so you can have an example of what kind of objects to store there:

src/utils/constants.ts
1// APP TEXT
2export const APP_TITLE = "Welcome, Developer"
3export const FOOTER_TEXT = `${new Date().getFullYear()} Built with ♡ by Welcome, Developer`
4// PAGES TITLE
5export const PAGE_TITLE_HOME = "Home"
6export const PAGE_TITLE_DASHBOARD = "Dashboard"
7export const PAGE_TITLE_GH_PRIVATE = "GitHub Private"
8export const PAGE_TITLE_GH_PUBLIC = "GitHub Public"
9export const PAGE_TITLE_CODE = "Code Editor"
10export const PAGE_TITLE_SETTINGS = "Settings"
11// UI CONSTANTS
12export const FOOTER_HEIGHT = 30
13export const HEADER_HEIGHT = 60
14export const DRAWER_WIDTH = 250

Nice, I decided to pick the Layout.tsx component as it's the one using most features and concepts. This component references some other components, so it is important that we create them first. Instead of pasting the JSX code in here, what about we copy it directly from the GitHub repository? So for some of you not yet familiar with navigating through GitHub repos, this could be a good exercise!

You'll need to copy all the components available from the path src/components on the GitHub repo. Don't worry, some of them such as the AppMenu.tsx references a couple of files we haven't talked about yet, but we will get there!

Next move, let's talk about the layout component. This is the component responsible for sharing a common layout between all the "pages" (I'll explain why the double-quotes in a sec 👌). Have a look at the code below, guys:

src/components/layout.tsx
1import React, { FC, ReactNode, useReducer } from "react";
2import clsx from "clsx";
3import { makeStyles, createStyles, Theme } from "@material-ui/core/styles";
4import { CssBaseline } from "@material-ui/core";
5
6// components
7import Header from "./Header";
8import Navigation from "./Navigation";
9import Footer from "./Footer";
10
11// constants
12import { DRAWER_WIDTH, FOOTER_HEIGHT } from "../utils/constants";
13
14// define css-in-js
15const useStyles = makeStyles((theme: Theme) =>
16 createStyles({
17 root: {
18 flex: 1,
19 display: "flex",
20 flexDirection: "column",
21 },
22 content: {
23 flexGrow: 1,
24 padding: theme.spacing(3),
25 minHeight: `calc(100vh - ${FOOTER_HEIGHT}px)`,
26 background: theme.palette.background.paper,
27 marginLeft: theme.spacing(7) + 1,
28 [theme.breakpoints.up("sm")]: {
29 marginLeft: theme.spacing(9) + 1,
30 },
31 },
32 toolbar: {
33 ...theme.mixins.toolbar,
34 },
35 contentShift: {
36 transition: theme.transitions.create("margin", {
37 easing: theme.transitions.easing.easeOut,
38 duration: theme.transitions.duration.enteringScreen,
39 }),
40 marginLeft: DRAWER_WIDTH,
41 },
42 })
43);
44
45// define interface to represent component props
46interface Props {
47 toggleTheme: () => void;
48 useDefaultTheme: boolean;
49 children: ReactNode;
50}
51
52// functional component
53const Layout: FC<Props> = ({ toggleTheme, useDefaultTheme, children }) => {
54 const classes = useStyles();
55 const [open, toggle] = useReducer((open) => !open, true);
56 return (
57 <div className={classes.root}>
58 <CssBaseline />
59 <Header
60 open={open}
61 handleMenuOpen={toggle}
62 toggleTheme={toggleTheme}
63 useDefaultTheme={useDefaultTheme}
64 />
65 <Navigation open={open} handleMenuClose={toggle} />
66 <main
67 className={clsx(classes.content, {
68 [classes.contentShift]: open,
69 })}
70 >
71 <div className={classes.toolbar} />
72 {children}
73 </main>
74 <footer>
75 <Footer />
76 </footer>
77 </div>
78 );
79};
80
81export default Layout;

Divide and conquer, everyone. Let's break the code into separate areas by row number:

  1. Rows 1-4: Importing the libraries required by React, Material-UI core and style, and clsx.
  2. Rows 7-9: Importing our app components used by the common layout, which is, used on every page.
  3. Row 12: Import constants from our constant file.
  4. Row 15-43: Defining the CSS-in-JS, which consists of declaring the CSS style in the JavaScript file directly. Functions such as makeStyles and createStyles are provided by the Material-UI library to create the style classes.
  5. Row 46-50: Defining the TypeScript interface that represents the properties the layout component expects to be informed;
  6. Row 52-79: A React Functional Component, where we define the layout of our application. Note in row 54, we are instantiating the CSS styles so it can be used as classes.
  7. Row 72: The children object represents the page content encapsulated by the layout. This means, every content informed as children to the layout, is going to be combined by the existing content of the parent component. This concept is known as Containment. For example, the Layout component could be used like below:
Example
1...
2 return (
3 <Layout>
4 <div>Child Content</div>
5 </Layout>
6 )
7 ...

Go have a look at the other React components in the application, you will find the same concepts and practices being applied there. Feel free to create new ones, and play with them!

Next, it's time to learn about how the user can navigate through pages in a single-page application. Wait, something does not sound right 🤔

Application Routes

A React application is therefore a single-page application, according to Wikipedia:

"A single-page application (SPA) is a web application or website that interacts with the user by dynamically rewriting the current web page with new data from the web server, instead of the default method of the browser loading entire new pages."

Alright, but we want to change the URL as the user clicks on an item in the navigation menu, or new content is presented, don't we? For this purpose, we're going to use the React Router library!

The React Router is a collection of navigational components to be used in the React applications. It enables us to create different routes based on the content of the pages. Remember the double-quotes on "pages" earlier, yep that's it!

To install it on your app, run the following npm command:

1npm install react-router-dom

The objective is to create page routes at the highest level of our application. Go to App.tsx, and apply the changes below:

src/App.tsx
1import React, { ReactElement, useReducer, FC } from "react";
2import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
3import { Helmet } from "react-helmet";
4
5// components
6import Layout from "./components/Layout";
7
8// app routes
9import { routes } from "./config";
10
11// constants
12import { APP_TITLE } from "./utils/constants";
13
14// interfaces
15import RouteItem from "./model/RouteItem.model";
16
17// default component
18const DefaultComponent: FC<{}> = (): ReactElement => (
19 <div>{`No Component Defined.`}</div>
20);
21
22function App() {
23
24 return (
25 <>
26 <Helmet>
27 <title>{APP_TITLE}</title>
28 </Helmet>
29 <Router>
30 <Switch>
31 <Layout>
32 {/* for each route config, a react route is created */}
33 {routes.map((route: RouteItem) => (
34 route.subRoutes ? route.subRoutes.map((item: RouteItem) => (
35 <Route
36 key={`${item.key}`}
37 path={`${item.path}`}
38 component={item.component || DefaultComponent}
39 exact
40 />
41 )) :
42 <Route
43 key={`${route.key}`}
44 path={`${route.path}`}
45 component={route.component || DefaultComponent}
46 exact
47 />
48 ))}
49 </Layout>
50 </Switch>
51 </Router>
52 </>
53 );
54}
55
56export default App;

A quick detail before starting explaining the React Router components. Notice the code of rows 26 to 28. That's the React Helmet, a handy component that manages the changes on the document page head. In this scenario, we're using it to change the page title according to the content.

Back to row 29, the Router is the highest component level when applying the routing. Then we have the Switch, which looks through its children Routes and renders the first one that matches the current URL.

So basically, a sample structure would be:

1return (
2 <Router>
3 <Switch>
4 <Route path="/about">
5 <About />
6 </Route>
7 <Route path="/users">
8 <Users />
9 </Route>
10 <Route path="/">
11 <Home />
12 </Route>
13 </Switch>
14 </Router>
15)

In the example above, it creates routes to the About, Users, and Home components. In the case of our application, instead of hard-coding the routes, we're importing it from an array existing in another file:

src/config/index.ts
1// icons
2import HomeIcon from '@material-ui/icons/Home';
3import DashboardIcon from '@material-ui/icons/BarChartOutlined';
4import CodeIcon from '@material-ui/icons/CodeOutlined';
5import SettingsIcon from '@material-ui/icons/SettingsOutlined';
6import GitHubIcon from '@material-ui/icons/GitHub';
7import PrivateIcon from '@material-ui/icons/LockOutlined';
8import PublicIcon from '@material-ui/icons/LockOpenOutlined';
9
10// components
11import Home from '../pages/Home';
12import Dashboard from '../pages/Dashboard';
13import GHPrivate from '../pages/GitHub/PrivateRepo';
14import GHPublic from '../pages/GitHub/PublicRepo';
15import CodeEditor from '../pages/CodeEditor';
16import Settings from '../pages/Settings';
17
18// interface
19import RouteItem from '../model/RouteItem.model';
20
21// define app routes
22export const routes: Array<RouteItem> = [
23 {
24 key: "router-home",
25 title: "Home",
26 tooltip: "Home",
27 path: "/",
28 enabled: true,
29 component: Home,
30 icon: HomeIcon,
31 appendDivider: true
32 },
33 {
34 key: "router-dashboard",
35 title: "Dashboard",
36 tooltip: "Dashboard",
37 path: "/dashboard",
38 enabled: true,
39 component: Dashboard,
40 icon: DashboardIcon
41 },
42 {
43 key: "router-gh",
44 title: "GitHub",
45 tooltip: "GitHub",
46 enabled: true,
47 icon: GitHubIcon,
48 subRoutes: [
49 {
50 key: "router-gh-private",
51 title: "Private Repos",
52 tooltip: "Private Repos",
53 path: "/gh/private",
54 enabled: true,
55 component: GHPrivate,
56 icon: PrivateIcon
57 }
58 , {
59 key: "router-gh-public",
60 title: "Public Repos",
61 tooltip: "Public Repos",
62 path: "/gh/public",
63 enabled: false,
64 component: GHPublic,
65 icon: PublicIcon
66 }
67 ]
68 },
69 {
70 key: "router-code",
71 title: "Code Editor",
72 tooltip: "Code Editor",
73 path: "/code-editor",
74 enabled: true,
75 component: CodeEditor,
76 icon: CodeIcon,
77 appendDivider: true
78 },
79 {
80 key: "router-settings",
81 title: "Settings",
82 tooltip: "Settings",
83 path: "/settings",
84 enabled: true,
85 component: Settings,
86 icon: SettingsIcon
87 },
88]

As you can see, the routes array is just the definition of a bunch of entities. Alright, but have a look at the highlighted lines. Each item in the array represents a RouteItem object. That's an interface created to cover all the properties useful to our app when working with a navigation menu:

1import { ComponentType, FC } from "react";
2
3// RouteItem is an interface for defining the application routes and navigation menu items
4interface RouteItem {
5 key: String
6 title: String
7 tooltip?: String
8 path?: String
9 component?: FC<{}>
10 enabled: boolean
11 icon?: ComponentType
12 subRoutes?: Array<RouteItem>
13 appendDivider?: boolean
14}
15
16export default RouteItem;

No worries, it becomes natural to you as you practice! Now, let's open the MenuItem.tsx component. Have a quick look at the highlighted lines below:

src/components/MenuItem.tsx
1import React, { FC, ReactElement } from "react";
2import clsx from "clsx";
3import {
4 ListItem,
5 ListItemText,
6 ListItemIcon,
7 Icon,
8 Tooltip,
9 IconButton,
10} from "@material-ui/core";
11import DefaultIcon from "@material-ui/icons/FileCopy";
12import { makeStyles, Theme, createStyles } from "@material-ui/core/styles";
13import { NavLink, useLocation } from "react-router-dom";
14
15// models
16import RouteItem from "../model/RouteItem.model";
17
18// define css-in-js
19const useStyles = makeStyles((theme: Theme) =>
20 createStyles({
21 selected: {
22 boxShadow:
23 theme.palette.type === "light"
24 ? "0 0 3px rgba(70,80,184,1), 0 0 9px rgba(70,80,184,1), 0 0 11px rgba(70,80,184,1), 0 0 30px rgba(70,80,184,1)"
25 : "0 0 3px #fc5a8d, 0 0 9px #fc5a8d, 0 0 11px #fc5a8d, 0 0 30px #fc5a8d",
26 },
27 nested: {
28 marginLeft: theme.spacing(2),
29 },
30 listItemDisabled: {
31 cursor: "not-allowed",
32 },
33 })
34);
35
36// functional component
37const MenuItem: FC<RouteItem> = (route: RouteItem): ReactElement => {
38 const classes = useStyles();
39 const location: any = useLocation();
40
41 const handleNavigate = (
42 e: React.MouseEvent<HTMLAnchorElement, MouseEvent>
43 ): void => {
44 if (!route.enabled) e.preventDefault();
45 };
46
47 return (
48 <>
49 <NavLink
50 to={`${route.path}`}
51 style={{ textDecoration: "none", color: "inherit" }}
52 key={`${route.key}`}
53 onClick={handleNavigate}
54 className={clsx({
55 [classes.listItemDisabled]: !route.enabled,
56 })}
57 >
58 <Tooltip title={route.tooltip || ""} placement="right">
59 <ListItem button disabled={!route.enabled}>
60 <ListItemIcon>
61 <IconButton
62 className={clsx({
63 [classes.selected]: location.pathname === route.path,
64 })}
65 size='small'
66 >
67 <Icon component={route.icon || DefaultIcon} />
68 </IconButton>
69 </ListItemIcon>
70 <ListItemText primary={route.title} />
71 </ListItem>
72 </Tooltip>
73 </NavLink>
74 </>
75 );
76};
77
78export default MenuItem;

The NavLink object from the React Router library is nothing more than just an HTML hyperlink, and a special version of the Link React Router component that will add styling attributes to the rendered element when it matches the current URL. We use it to redirect the user to different page content (or component).

Back to our GitHub repo, copy the content of the page folder to your app. In there, there are the components we want our page routes to redirect to. You'll find out that they are just React components, but it's better to keep them separately in a different folder than src/components, for organization purposes.

Spend some time understanding the structure put in place in regards to importing the route array from the config file, passing them through the components, and reading its properties. At the moment, the route configuration is being defined in a TypeScript file (src/config/index.tx), but let's say maybe it could be read from a database? Or some cloud storage text file? Who knows! One thing is important to know, always try to make it the most dynamic and flexible possible, guys (without overcomplicating this, of course) 👍

Context Management

The official definition couldn't be more self-explanatory:

React Context provides a way to pass data through the component tree without having to pass props down manually at every level.

Context is a pretty useful approach to share global data among every component of our application. There is a lot to discuss, and I would even say, it could be worth a post just for it. Have a look at the App.tsx file, developers:

src/App.tsx
1import React, { ReactElement, useReducer, FC } from "react";
2import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
3import { Helmet } from "react-helmet";
4
5// components
6import Layout from "./components/Layout";
7
8// app routes
9import { routes } from "./config";
10
11// constants
12import { APP_TITLE } from "./utils/constants";
13
14// interfaces
15import RouteItem from "./model/RouteItem.model";
16
17// define app context
18const AppContext = React.createContext(null);
19
20// default component
21const DefaultComponent: FC<{}> = (): ReactElement => (
22 <div>{`No Component Defined.`}</div>
23);
24
25function App() {
26 return (
27 <>
28 <Helmet>
29 <title>{APP_TITLE}</title>
30 </Helmet>
31 <AppContext.Provider value={null}>
32 <ThemeProvider theme={theme}>
33 <Router>
34 <Switch>
35 <Layou>
36 {/* for each route config, a react route is created */}
37 {routes.map((route: RouteItem) => (
38 route.subRoutes ? route.subRoutes.map((item: RouteItem) => (
39 <Route
40 key={`${item.key}`}
41 path={`${item.path}`}
42 component={item.component || DefaultComponent}
43 exact
44 />
45 )) :
46 <Route
47 key={`${route.key}`}
48 path={`${route.path}`}
49 component={route.component || DefaultComponent}
50 exact
51 />
52 ))}
53 </Layou>
54 </Switch>
55 </Router>
56 </ThemeProvider>
57 </AppContext.Provider>
58 </>
59 );
60}
61
62export default App;

The value property stores the objects that can be accessed by the components below the AppContext.Provider component. In our boilerplate app, I'm not passing any object to the value property because my objective is just to show how it can be implemented.

Custom Themes

As a bonus, our React app enables the users to toggle between a light ☀️ and a dark 🌙 theme! Probably you have already seen it on many websites out there. It's cool, isn't it?

A few changes only are required to have it implemented, using the Material-UI library. On the GitHub repo, open the Theme file (src/theme/appTheme.ts):

src/theme/AppTheme.ts
1import { createMuiTheme, Theme } from '@material-ui/core/styles';
2import blue from '@material-ui/core/colors/blue';
3
4// define light theme colors
5export const lightTheme: Theme = createMuiTheme({
6 palette: {
7 type: "light",
8 },
9});
10
11// define dark theme colors
12export const darkTheme: Theme = createMuiTheme({
13 palette: {
14 type: "dark",
15 primary: {
16 main: "#fc5a8d",
17 },
18 secondary: {
19 main: blue[500],
20 },
21 },
22});

Using the createMuiTheme function, a Theme is defined according to the library standards. There are plenty of options available to be customized, check the documentation to explore more!

On the App.tsx component, the following changes are made:

src/App.tsx
1import React, { ReactElement, useReducer, FC } from "react";
2import {
3 createMuiTheme,
4 Theme,
5 responsiveFontSizes,
6 ThemeProvider,
7} from "@material-ui/core/styles";
8import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
9import { Helmet } from "react-helmet";
10
11// components
12import Layout from "./components/Layout";
13
14// theme
15import { lightTheme, darkTheme } from "./theme/appTheme";
16
17// app routes
18import { routes } from "./config";
19
20// constants
21import { APP_TITLE } from "./utils/constants";
22
23// interfaces
24import RouteItem from "./model/RouteItem.model";
25
26// define app context
27const AppContext = React.createContext(null);
28
29// default component
30const DefaultComponent: FC<{}> = (): ReactElement => (
31 <div>{`No Component Defined.`}</div>
32);
33
34function App() {
35 const [useDefaultTheme, toggle] = useReducer((theme) => !theme, true);
36
37 // define custom theme
38 let theme: Theme = createMuiTheme(useDefaultTheme ? lightTheme : darkTheme);
39 theme = responsiveFontSizes(theme);
40
41 return (
42 <>
43 <Helmet>
44 <title>{APP_TITLE}</title>
45 </Helmet>
46 <AppContext.Provider value={null}>
47 <ThemeProvider theme={theme}>
48 <Router>
49 <Switch>
50 <Layout toggleTheme={toggle} useDefaultTheme={useDefaultTheme}>
51 {/* for each route config, a react route is created */}
52 {routes.map((route: RouteItem) => (
53 route.subRoutes ? route.subRoutes.map((item: RouteItem) => (
54 <Route
55 key={`${item.key}`}
56 path={`${item.path}`}
57 component={item.component || DefaultComponent}
58 exact
59 />
60 )) :
61 <Route
62 key={`${route.key}`}
63 path={`${route.path}`}
64 component={route.component || DefaultComponent}
65 exact
66 />
67 ))}
68 </Layout>
69 </Switch>
70 </Router>
71 </ThemeProvider>
72 </AppContext.Provider>
73 </>
74 );
75}
76
77export default App;

The ThemeProvider component applies the custom theme, informed on the theme property, to the components below it.

Next Steps

We could spend days or even months only talking about TypeScript and React, guys. There is plenty to explore, learn, and practice. My intention when building this starter application is to cover some basic topics, save us some time on future posts' exercises, and provide you, followers, with a step ahead when starting your own apps. Hopefully, it'll be a mission accomplished 😃

If I may suggest to you what is next, it would be...practice practice practice! Start React TypeScript applications from scratch, explore the long list of components on the Material-UI web page, and combine them, play with the React router. The possibilities are many, and it only depends on you to crush them!

Useful Resources

Comments / Feedback

I would love to hear your feedback about this post, as well as the first project created by the Welcome, Developer team! Not only limited to it, but also about the work we have been doing together over this busy year 🔥 Please, you are welcome to flick me a message through my email technology.castro@gmail.com.

Conclusion

That's all for today, folks! I hope you have had a great time, enjoyed yourself, and learned some cool stuff 👍

Thank you very much for getting to the end of the post, developers. I really appreciate it!

This is the last post of this year of 2020, guys! It might be a good time for a vacation ⛱️ We're back in early January next year with more exciting posts and projects for all of you!

I want to wish you a Merry Christmas 🎄 and a Happy New Year 🍾 🎆

Take it easy, and enjoy the time with your loved ones ♡

I'm truly grateful to everyone that has been following us throughout the year! See you all in the next one, developers!

Follow Us

The Welcome, Developer has a brand new LinkedIn page! If you enjoy our content, we would be very happy to have you as a follower! In there, we will update you about new posts, new projects, and all the cool stuff!

© 2021 Welcome, Developer. All rights reserved.
Proudly made in New Zealand ♡