Introductory tutorial
Learn how to import and style Joy UI components to build a simple login page with light and dark modes.
This tutorial will walk you through how to assemble the UI for a basic login page using Joy UI.
You'll be introduced to several common components as well as some of the props you can use to control their styles.
You'll also encounter key features of Joy UI such as global variants, the sx
prop, and the useColorScheme
hook.
By the end, you should understand how to:
- import and customize Joy UI components
- add styles to Joy UI components with
sx
- override default HTML elements with
component
- toggle light and dark mode with
useColorScheme
Interactive demo
Here's what the final product looks like—click on the <> icon underneath the demo to see the full source code:
Prerequisites
This tutorial assumes that you've already:
- set up a React app—try Create React App if you need a boilerplate
- installed Joy UI in your app—see Installation for instructions
Import the Sheet component for structure
The Sheet component is a <div>
container that supports Joy UI's global variants feature, helping to ensure consistency across your app.
Import Sheet and add it to your app as shown below.
(If you're using Create React App, for example, all of this code should go in App.js
.)
Notice that Joy UI components must be nested within <CssVarsProvider />
:
import * as React from 'react';
import { CssVarsProvider } from '@mui/joy/styles';
import Sheet from '@mui/joy/Sheet';
export default function App() {
return (
<CssVarsProvider>
<Sheet variant="outlined">Welcome!</Sheet>
</CssVarsProvider>
);
}
Add styles with the sx prop
All Joy UI components accept the sx
prop, which gives you access to a shorthand syntax for writing CSS.
It's great for creating one-off customizations or rapidly experimenting with different styles.
Replace your basic Sheet from the previous step with the following sx
-styled Sheet:
<Sheet
sx={{
width: 300,
mx: 'auto', // margin left & right
my: 4, // margin top & botom
py: 3, // padding top & bottom
px: 2, // padding left & right
display: 'flex',
flexDirection: 'column',
gap: 2,
borderRadius: 'sm',
boxShadow: 'md',
}}
>
Welcome!
</Sheet>
Add text with the Typography component
The Typography component replaces HTML header, paragraph, and span tags to help maintain a consistent hierarchy of text on the page.
Add an import for Typography with the rest of your imports:
import Typography from '@mui/joy/Typography';
Replace Welcome!
inside your Sheet component with this <div>
:
<div>
<Typography level="h4" component="h1">
Welcome!
</Typography>
<Typography level="body2">Sign in to continue.</Typography>
</div>
Add Text Field for user inputs
The Text Field component bundles together the Form Control, Form Label, Input, and Form Helper Text components to provide you with a sophisticated field for user input.
Add an import for Text Field with the rest of your imports:
import TextField from '@mui/joy/TextField';
Insert these two Text Fields below the <div>
from the previous step, inside the Sheet:
<TextField
// html input attribute
name="email"
type="email"
placeholder="johndoe@email.com"
// pass down to FormLabel as children
label="Email"
/>
<TextField
name="password"
type="password"
placeholder="password"
label="Password"
/>
Import Button and Link for user actions
The Button and Link components replace the HTML <button>
and <a>
tags, respectively, giving you access to global variants, the sx
and component
props, and more.
Add the following imports with the rest in your app:
import Button from '@mui/joy/Button';
import Link from '@mui/joy/Link';
Add the following Button, Typography, and Link components after the Text Fields from the previous step, still nested inside the Sheet.
Notice that the Link is appended to the Typography inside of the endDecorator
prop:
<Button sx={{ mt: 1 /* margin top */ }}>
Log in
</Button>
<Typography
endDecorator={<Link href="/sign-up">Sign up</Link>}
fontSize="sm"
sx={{ alignSelf: 'center' }}
>
Don't have an account?
</Typography>
🎁 Bonus: Build a toggle for light and dark mode
The useColorScheme
hook aids in the implementation of a toggle button for switching between light and dark mode in an app.
It also enables Joy UI to ensure that the user-selected mode (which is stored in localStorage
by default) stays in sync across browser tabs.
Add useColorScheme
to your import from @mui/joy/styles
:
import { CssVarsProvider, useColorScheme } from '@mui/joy/styles';
Next, create a light/dark mode toggle button by adding the following code snippet in between your imports and your App()
:
function ModeToggle() {
const { mode, setMode } = useColorScheme();
const [mounted, setMounted] = React.useState(false);
// necessary for server-side rendering
// because mode is undefined on the server
React.useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null;
}
return (
<Button
variant="outlined"
onClick={() => {
setMode(mode === 'light' ? 'dark' : 'light');
}}
>
{mode === 'light' ? 'Turn dark' : 'Turn light'}
</Button>
);
}
Finally, add your newly built <ModeToggle />
button above <Sheet />
:
export default function App() {
return (
<CssVarsProvider>
+ <ModeToggle />
<Sheet>...</Sheet>
</CssVarsProvider>
);
}
Your app should now look like the interactive demo at the top of the page. Great job making it all the way to the end!
Summary
Here's a recap of the components used:
Here are some of the major features introduced:
Next steps
This tutorial does not cover theming or general component customization. Learn more about different customization approaches and when to apply them.
To see some more sophisticated examples of Joy UI in action, check out our collection of templates.
Are you migrating from Material UI? Learn how to work with Joy UI and Material UI together in one app.