Skip to content

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:

  1. import and customize Joy UI components
  2. add styles to Joy UI components with sx
  3. override default HTML elements with component
  4. 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:

Welcome!

Sign in to continue.

Don't have an account?Sign up

Prerequisites

This tutorial assumes that you've already:

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.)

<span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> CssVarsProvider <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@mui/joy/styles'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> Sheet <span class="token keyword">from</span> <span class="token string">'@mui/joy/Sheet'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">App</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">CssVarsProvider</span></span><span class="token punctuation">></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Sheet</span></span> <span class="token attr-name">variant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>outlined<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Welcome<span class="token operator">!</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Sheet</span></span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">CssVarsProvider</span></span><span class="token punctuation">></span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

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:

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Sheet</span></span>
  <span class="token attr-name">sx</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span>
    <span class="token literal-property property">width</span><span class="token operator">:</span> <span class="token number">300</span><span class="token punctuation">,</span>
    <span class="token literal-property property">mx</span><span class="token operator">:</span> <span class="token string">'auto'</span><span class="token punctuation">,</span> <span class="token comment">// margin left &amp; right</span>
    <span class="token literal-property property">my</span><span class="token operator">:</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token comment">// margin top &amp; bottom</span>
    <span class="token literal-property property">py</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token comment">// padding top &amp; bottom</span>
    <span class="token literal-property property">px</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token comment">// padding left &amp; right</span>
    <span class="token literal-property property">display</span><span class="token operator">:</span> <span class="token string">'flex'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">flexDirection</span><span class="token operator">:</span> <span class="token string">'column'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">gap</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
    <span class="token literal-property property">borderRadius</span><span class="token operator">:</span> <span class="token string">'sm'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">boxShadow</span><span class="token operator">:</span> <span class="token string">'md'</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">}</span></span>
<span class="token punctuation">></span></span>
  Welcome<span class="token operator">!</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Sheet</span></span><span class="token punctuation">></span></span>

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:

<span class="token keyword">import</span> Typography <span class="token keyword">from</span> <span class="token string">'@mui/joy/Typography'</span><span class="token punctuation">;</span>

Replace Welcome! inside your Sheet component with this <div>:

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Typography</span></span> <span class="token attr-name">level</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>h4<span class="token punctuation">"</span></span> <span class="token attr-name">component</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>h1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
    Welcome<span class="token operator">!</span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Typography</span></span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Typography</span></span> <span class="token attr-name">level</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>body2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sign <span class="token keyword">in</span> to <span class="token keyword">continue</span><span class="token punctuation">.</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Typography</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>

Add text field for user inputs

The Form Control, Form Label, and Input components can be used together to provide you with a sophisticated field for user input.

Add imports for Form Control, Form Label, and Input with the rest of your imports:

<span class="token keyword">import</span> FormControl <span class="token keyword">from</span> <span class="token string">'@mui/joy/FormControl'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> FormLabel <span class="token keyword">from</span> <span class="token string">'@mui/joy/FormLabel'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> Input <span class="token keyword">from</span> <span class="token string">'@mui/joy/Input'</span><span class="token punctuation">;</span>

Insert these two text fields below the <div> from the previous step, inside the Sheet:

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">FormControl</span></span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">FormLabel</span></span><span class="token punctuation">></span></span>Email<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">FormLabel</span></span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Input</span></span>
    <span class="token comment">// html input attribute</span>
    <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span>
    <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span>
    <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>johndoe@email.com<span class="token punctuation">"</span></span>
  <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">FormControl</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">FormControl</span></span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">FormLabel</span></span><span class="token punctuation">></span></span>Password<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">FormLabel</span></span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Input</span></span>
    <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>password<span class="token punctuation">"</span></span>
    <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>password<span class="token punctuation">"</span></span>
    <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>password<span class="token punctuation">"</span></span>
  <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">FormControl</span></span><span class="token punctuation">></span></span>

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:

<span class="token keyword">import</span> Button <span class="token keyword">from</span> <span class="token string">'@mui/joy/Button'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> Link <span class="token keyword">from</span> <span class="token string">'@mui/joy/Link'</span><span class="token punctuation">;</span>

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:

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Button</span></span> <span class="token attr-name">sx</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token literal-property property">mt</span><span class="token operator">:</span> <span class="token number">1</span> <span class="token comment">/* margin top */</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span>
  Log <span class="token keyword">in</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Button</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Typography</span></span>
  <span class="token attr-name">endDecorator</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Link</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/sign-up<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sign up<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Link</span></span><span class="token punctuation">></span></span><span class="token punctuation">}</span></span>
  <span class="token attr-name">fontSize</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sm<span class="token punctuation">"</span></span>
  <span class="token attr-name">sx</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token literal-property property">alignSelf</span><span class="token operator">:</span> <span class="token string">'center'</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></span>
<span class="token punctuation">></span></span>
  Don't have an account<span class="token operator">?</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Typography</span></span><span class="token punctuation">></span></span>

🎁 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:

<span class="token keyword">import</span> <span class="token punctuation">{</span> CssVarsProvider<span class="token punctuation">,</span> useColorScheme <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@mui/joy/styles'</span><span class="token punctuation">;</span>

Next, create a light/dark mode toggle button by adding the following code snippet in between your imports and your App():

<span class="token keyword">function</span> <span class="token function">ModeToggle</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> mode<span class="token punctuation">,</span> setMode <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useColorScheme</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>mounted<span class="token punctuation">,</span> setMounted<span class="token punctuation">]</span> <span class="token operator">=</span> React<span class="token punctuation">.</span><span class="token function">useState</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// necessary for server-side rendering</span>
  <span class="token comment">// because mode is undefined on the server</span>
  React<span class="token punctuation">.</span><span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token function">setMounted</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>mounted<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Button</span></span>
      <span class="token attr-name">variant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>outlined<span class="token punctuation">"</span></span>
      <span class="token attr-name">onClick</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
        <span class="token function">setMode</span><span class="token punctuation">(</span>mode <span class="token operator">===</span> <span class="token string">'light'</span> <span class="token operator">?</span> <span class="token string">'dark'</span> <span class="token operator">:</span> <span class="token string">'light'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">}</span></span>
    <span class="token punctuation">></span></span>
      <span class="token punctuation">{</span>mode <span class="token operator">===</span> <span class="token string">'light'</span> <span class="token operator">?</span> <span class="token string">'Turn dark'</span> <span class="token operator">:</span> <span class="token string">'Turn light'</span><span class="token punctuation">}</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Button</span></span><span class="token punctuation">></span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

Finally, add your newly built <ModeToggle /> button above <Sheet />:

<span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line">export default function App() {
</span><span class="token prefix unchanged"> </span><span class="token line">  return (
</span><span class="token prefix unchanged"> </span><span class="token line">    &lt;CssVarsProvider>
</span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line">      &lt;ModeToggle />
</span></span><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line">      &lt;Sheet>...&lt;/Sheet>
</span><span class="token prefix unchanged"> </span><span class="token line">    &lt;/CssVarsProvider>
</span><span class="token prefix unchanged"> </span><span class="token line">  );
</span><span class="token prefix unchanged"> </span><span class="token line">}</span></span>

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.