Nextjs + Mantine
How to use the Mantine library with the nextjs 13?
Design a Modern website with Mantine UI and Hooks.
--
I used so many UI component libraries in my past life as a front-end developer. Last Time I was impressed with Github Primer, and I never did something like that. But recently found one more library that changes my life. It provides many features out of the box, and no other library provides that. The Libaray name is Mantine. Mantine is more suitable for all kinds of small and large-scale websites.
Some of the cool features which I like are that is
- 55 Ready-made react hooks
- 134 Ready-made components
- Inbuilt form handling and design
- Inbuilt date and time handling
Investing Time into learning Mantine Library is a good approach. For large-scale websites, one UI library does not solve all problems. But the other hand Mantine UI library provides modern components, for example, Prism code highlight, spotlight, dropzone, TypographyStylesProvider, etc.
Inbuilt Mantine Hook solves every problem related to your project. Mantine provides a bunch of hooks, for example, pagination handling, Toggle, use-input-state, etc
All the code is available on GitHub. You can also check out the live demo website.
Let’s start
· Setup new Nextjs Project
· Installation
∘ Mantine Packages List
· Configuration
· How to enable the theme toggle in mantine?
· How to build the Header with mentine?
· Conclusion
Setup new Nextjs Project
The first step is to create a new nextjs application or project with create-next-app to start work with the Matine UI library.
To create a new nextjs app, run the following command and install the tailwind css and typescript.
npx create-next-app@latest
# or
yarn create next-app
# or
pnpm create next-app
rajdeepsingh@officialrajdeepsingh:~/open-sources$ pnpm create next-app@latest
.../share/pnpm/store/v3/tmp/dlx-344317 | +1 +
Packages are hard linked from the content-addressable store to the virtual store.
Content-addressable store is at: /home/rajdeepsingh/.local/share/pnpm/store/v3
Virtual store is at: ../.local/share/pnpm/store/v3/tmp/dlx-344317/node_modules/.pnpm
.../share/pnpm/store/v3/tmp/dlx-344317 | Progress: resolved 1, reused 0, downloaded 1, added 1, done
✔ What is your project named? … mantine-ui-nextjs
✔ Would you like to use TypeScript with this project? … No / Yes
✔ Would you like to use ESLint with this project? … No / Yes
✔ Would you like to use Tailwind CSS with this project? … No / Yes
✔ Would you like to use `src/` directory with this project? … No / Yes
✔ Would you like to use experimental `app/` directory with this project? … No / Yes
✔ What import alias would you like configured? … @/*
Creating a new Next.js app in /home/rajdeepsingh/open-sources/mantine-ui-nextjs.
Using pnpm.
Initializing project with template: app-tw
Installing dependencies:
- react
- react-dom
- next
- typescript
- @types/react
- @types/node
- @types/react-dom
- tailwindcss
- postcss
- autoprefixer
- eslint
- eslint-config-next
Packages: +322
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Packages are hard linked from the content-addressable store to the virtual store.
Content-addressable store is at: /home/rajdeepsingh/.local/share/pnpm/store/v3
Virtual store is at: node_modules/.pnpm
Downloading registry.npmjs.org/next/13.3.2: 12.1 MB/12.1 MB, done
Downloading registry.npmjs.org/@next/swc-linux-x64-musl/13.3.2: 37.8 MB/37.8 MB, done
Downloading registry.npmjs.org/@next/swc-linux-x64-gnu/13.3.2: 37.9 MB/37.9 MB, done
Progress: resolved 330, reused 303, downloaded 19, added 322, done
dependencies:
+ @types/node 18.16.3
+ @types/react 18.2.0
+ @types/react-dom 18.2.1
+ autoprefixer 10.4.14
+ eslint 8.39.0
+ eslint-config-next 13.3.2
+ next 13.3.2
+ postcss 8.4.23
+ react 18.2.0
+ react-dom 18.2.0
+ tailwindcss 3.3.2
+ typescript 5.0.4
The integrity of 4210 files was checked. This might have caused installation to take longer.
Done in 7m 42.8s
Initialized a git repository.
Success! Created mantine-ui-nextjs at /home/rajdeepsingh/open-sources/mantine-ui-nextjs
rajdeepsingh@officialrajdeepsingh:~/open-sources$
I choose tailwind CSS to speed up the design process with nextjs and Mantine UI library.
Installation
The entire Mantine UI library divides into packages, and developers easily install Mantine packages according to their requirements.
Mantine Packages List
- @mantine/hooks: Hooks for state and UI management
- @mantine/core: Core components library: inputs, buttons, overlays, etc.
- @mantine/form: Form management library.
- @mantine/dates: Date inputs, calendars
- @mantine/notifications: Notifications system
- @mantine/prism: Code highlight with your theme colors and styles
- @mantine/tiptap: Rich text editor based on Tiptap
- @mantine/dropzone: Capture files with drag and drop
- @mantine/carousel: Embla-based carousel component
- @mantine/spotlight: The overlay command with command + k.
- @mantine/modals: Centralized models manager
- @mantine/nprogress: Navigation progress
Due to the different package varieties, Mantine provides an installation page where you select and remove the package and convert them into one command.
For Nextjs, to start work with mantine, you need the following compulsory package to install in your project.
yarn add @mantine/core @mantine/hooks @mantine/next @emotion/server @emotion/react
# or
yarn add @mantine/core @mantine/hooks @mantine/next @emotion/server @emotion/react
# or
yarn add @mantine/core @mantine/hooks @mantine/next @emotion/server @emotion/react
Configuration
Configuring the mantine UI library with nextjs is a straightforward process, just like a copy-paste of code.
Replace your existing _document.tsx
file code with the following code.
// _document.tsx copy and paste
import { createGetInitialProps } from '@mantine/next';
import Document, { Head, Html, Main, NextScript } from 'next/document';
const getInitialProps = createGetInitialProps();
export default class _Document extends Document {
static getInitialProps = getInitialProps;
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
Replace your current _app.tsx
code with the following code.
// _app.tsx
import { AppProps } from 'next/app';
import Head from 'next/head';
import { MantineProvider } from '@mantine/core';
export default function App(props: AppProps) {
const { Component, pageProps } = props;
return (
<MantineProvider
withGlobalStyles
withNormalizeCSS
theme={{
/** Put your mantine theme override here light or dark theme */
colorScheme: 'light',
}}
>
<Component {...pageProps} />
</MantineProvider>
);
}
Now the configuration part is done. To test your nextjs app, copy and paste the following code into your index.tsx
file.
// pages/index.tsx
import Head from "next/head";
import { Box, Center, Container } from "@mantine/core";
export default function Home() {
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Container className="box">
<Center h={724} mx="auto">
<Box>All elements inside Center are centered in Container.</Box>
</Center>
</Container>
</>
);
}
Change the background color. I write some css in the `globals.css` file.
Your nextjs demo app looks like in a browser to run a local development server with npm run dev
.
How to enable the theme toggle in mantine?
To create a theme toggle with mantine, you need to change some code in _app.tsx
file.
// import Tailwind CSS file
import '@/styles/globals.css';
import type { AppProps } from 'next/app';
import { useLocalStorage } from '@mantine/hooks';
// ColorSchemeProvider help to enable toggle
import { MantineProvider, ColorSchemeProvider, ColorScheme } from '@mantine/core';
export default function App({ Component, pageProps }: AppProps) {
// set theme in local store
const [colorScheme, setColorScheme] = useLocalStorage<ColorScheme>({
key: 'demo-color-scheme',
defaultValue: 'light',
getInitialValueInEffect: true,
});
// create theme toggle
const toggleColorScheme = (value?: ColorScheme) => setColorScheme(value || (colorScheme === 'dark' ? 'light' : 'dark'));
return (
<ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
<MantineProvider withGlobalStyles withNormalizeCSS theme={{ colorScheme }}>
<Component {...pageProps} />
</MantineProvider>
</ColorSchemeProvider>
)
}
To toggle the theme from light to dark, you need to use the useMantineColorScheme
hook import from the @mantine/core
package.
import { createStyles, Header, Group, ActionIcon, Container, Burger, rem } from '@mantine/core';
// import icons from @tabler/icons-react
import { IconBrandTwitter, IconBrandLinkedin, IconBrandGithub, IconSun, IconMoonStars } from '@tabler/icons-react';
// import useMantineColorScheme hook to toggle theme
import { useMantineColorScheme } from '@mantine/core';
import Link from 'next/link';
const useStyles = createStyles((theme) => ({
// some css
})));
interface HeaderMiddleProps {
links: { link: string; label: string }[];
}
export function HeaderMenu({ links }: HeaderMiddleProps) {
const { classes, cx } = useStyles();
// toggle theme between light and dark
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
const dark = colorScheme === 'dark';
return (
<Header height={56} mb={120}>
<Container className={classes.inner}>
<Group position='center'>
{/* ... rest of code */}
<Group spacing={0} className={classes.social} position="center" noWrap>
{/* ... rest of code */}
<Link target='_blank' href="https://twitter.com/Official_R_deep">
<ActionIcon size="lg">
<IconBrandTwitter size="1.1rem" stroke={1.5} />
</ActionIcon>
</Link>
<Link target='_blank' href="https://www.linkedin.com/in/officalrajdeepsingh/">
<ActionIcon size="lg">
<IconBrandLinkedin size="1.1rem" stroke={1.5} />
</ActionIcon>
</Link>
<Link target='_blank' href="http://github.com/officialrajdeepsingh">
<ActionIcon size="lg">
<IconBrandGithub size="1.1rem" stroke={1.5} />
</ActionIcon>
</Link>
<SearchBar />
{/* Theme toggling and changing icon */}
<ActionIcon
size="lg"
variant="outline"
color={dark ? 'yellow' : 'blue'}
onClick={() => toggleColorScheme()}
title="Toggle color scheme"
>
{dark ? <IconSun size="1.1rem" /> : <IconMoonStars size="1.1rem" />}
</ActionIcon>
</Group>
</Group>
</Container>
</Header>
);
}
How to build the Header with mentine?
The Mantine provides inbuilt, ready-made open-source 134 responsive components.
Then I click the Headers section.
Select one of the header components which are you like for the site. Then copy the code and paste it into your file.
// Header.tsx
import { useState } from 'react';
import { createStyles, Header, Group, ActionIcon, Container, Burger, rem } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { IconBrandTwitter, IconBrandYoutube, IconBrandInstagram } from '@tabler/icons-react';
import { MantineLogo } from '@mantine/ds';
const useStyles = createStyles((theme) => ({
inner: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
height: rem(56),
[theme.fn.smallerThan('sm')]: {
justifyContent: 'flex-start',
},
},
links: {
width: rem(260),
[theme.fn.smallerThan('sm')]: {
display: 'none',
},
},
social: {
width: rem(260),
[theme.fn.smallerThan('sm')]: {
width: 'auto',
marginLeft: 'auto',
},
},
burger: {
marginRight: theme.spacing.md,
[theme.fn.largerThan('sm')]: {
display: 'none',
},
},
link: {
display: 'block',
lineHeight: 1,
padding: `${rem(8)} ${rem(12)}`,
borderRadius: theme.radius.sm,
textDecoration: 'none',
color: theme.colorScheme === 'dark' ? theme.colors.dark[0] : theme.colors.gray[7],
fontSize: theme.fontSizes.sm,
fontWeight: 500,
'&:hover': {
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
},
},
linkActive: {
'&, &:hover': {
backgroundColor: theme.fn.variant({ variant: 'light', color: theme.primaryColor }).background,
color: theme.fn.variant({ variant: 'light', color: theme.primaryColor }).color,
},
},
}));
interface HeaderMiddleProps {
links: { link: string; label: string }[];
}
export function HeaderMiddle({ links }: HeaderMiddleProps) {
const [opened, { toggle }] = useDisclosure(false);
const [active, setActive] = useState(links[0].link);
const { classes, cx } = useStyles();
const items = links.map((link) => (
<a
key={link.label}
href={link.link}
className={cx(classes.link, { [classes.linkActive]: active === link.link })}
onClick={(event) => {
event.preventDefault();
setActive(link.link);
}}
>
{link.label}
</a>
));
return (
<Header height={56} mb={120}>
<Container className={classes.inner}>
<Burger opened={opened} onClick={toggle} size="sm" className={classes.burger} />
<Group className={classes.links} spacing={5}>
{items}
</Group>
<MantineLogo size={28} />
<Group spacing={0} className={classes.social} position="right" noWrap>
<ActionIcon size="lg">
<IconBrandTwitter size="1.1rem" stroke={1.5} />
</ActionIcon>
<ActionIcon size="lg">
<IconBrandYoutube size="1.1rem" stroke={1.5} />
</ActionIcon>
<ActionIcon size="lg">
<IconBrandInstagram size="1.1rem" stroke={1.5} />
</ActionIcon>
</Group>
</Container>
</Header>
);
}
Then import your Header on _app.tsx
or your layout file. Then use it.
import '@/styles/globals.css';
import type { AppProps } from 'next/app';
import { useLocalStorage } from '@mantine/hooks';
import { MantineProvider, ColorSchemeProvider, ColorScheme } from '@mantine/core';
// Import Header component
import { HeaderMiddle } from '@/components/Header/Header';
let links = [ { link: "/", label: "Home" }, { link: "/about", label: "About us" }, { link: "/contact", label: "Contact us" }];
export default function App({ Component, pageProps }: AppProps) {
const [colorScheme, setColorScheme] = useLocalStorage<ColorScheme>({
key: 'demo-color-scheme',
defaultValue: 'light',
getInitialValueInEffect: true,
});
const toggleColorScheme = (value?: ColorScheme) => setColorScheme(value || (colorScheme === 'dark' ? 'light' : 'dark'));
return (
<ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
<MantineProvider withGlobalStyles withNormalizeCSS theme={{ colorScheme }}>
{/* Use it and nav links */}
<HeaderMiddle links={links} />
<Component {...pageProps} />
</MantineProvider>
</ColorSchemeProvider>
)
}
Note
Currently, Mantine is not ready for the nextjs app directory.
Conclusion
Mantine is an excellent library for building small to large-scale websites with nextjs. It speeds up the development process and provides a modern look similar to the MUI library.
I promise if you use the Mantine library, you never need any other UI library and packages for the site.
You can use Tailwind CSS with mantine. But you can’t utilize Tailwind CSS dark mode darkMode:”class”
class-based functionality. You can only use darkMode:”media”
functionality.
You can share and follow us on Twitter and Linkedin. If you like my work, please read more content on the officialrajdeepsingh.dev, frontend web, and Sign up for a free newsletter.
You can also check out awesome-next, a curated list of awesome Nextjs-based libraries that help build small and large-scale applications with next.js.