DEV Community

Cover image for Mantine UI: A Complete Guide to Building Modern Web Interfaces
Sarthak Niranjan for CodeParrot

Posted on • Originally published at codeparrot.ai

Mantine UI: A Complete Guide to Building Modern Web Interfaces

Mantine UI is a comprehensive React component library designed to help developers build feature-rich and responsive web applications with minimal effort. In this blog, we will explore the core features of Mantine UI, how it compares to other libraries, and how you can use it to improve your web development projects.

Mantine UI Image

Why Consider Using Mantine UI?

  1. Speed Up Development: With over 123 responsive components, Mantine UI allows you to build websites quickly using pre-made, customizable elements.
  2. Flexibility: All components are built with the Mantine theme in mind, making it easy to change colors, fonts, shadows, and other properties to match your design needs.
  3. Light and Dark Mode Support: Most components come with built-in support for both light and dark themes, so you don’t need to make any additional adjustments to accommodate different color schemes.
  4. Free and Open Source: Mantine UI is free, open-source, and licensed under MIT, meaning you can use it in any project, including commercial ones, without any restrictions.

Types of Components Available in Mantine UI

Mantine UI provides a rich selection of components tailored for different use cases, allowing developers to quickly build modern and responsive interfaces. Here, we break down three major categories of components—Application UI, Page Sections, and Blog UI. Each category includes multiple sub-types, designed to address specific parts of your web project.

1. Application UI

Application UI components are the building blocks for creating feature-rich user interfaces. These components include everything from navigational elements to interactive forms.

• Example: Navbar With Nested Links

Application UI Image

Code: NavbarNested.tsx

import { Group, Code, ScrollArea, rem } from '@mantine/core';
import {
  IconNotes,
  IconCalendarStats,
  IconGauge,
  IconPresentationAnalytics,
  IconFileAnalytics,
  IconAdjustments,
  IconLock,
} from '@tabler/icons-react';
import { UserButton } from '../UserButton/UserButton';
import { LinksGroup } from '../NavbarLinksGroup/NavbarLinksGroup';
import { Logo } from './Logo';
import classes from './NavbarNested.module.css';


const mockdata = [
  { label: 'Dashboard', icon: IconGauge },
  {
    label: 'Market news',
    icon: IconNotes,
    initiallyOpened: true,
    links: [
      { label: 'Overview', link: '/' },
      { label: 'Forecasts', link: '/' },
      { label: 'Outlook', link: '/' },
      { label: 'Real time', link: '/' },
    ],
  },
  {
    label: 'Releases',
    icon: IconCalendarStats,
    links: [
      { label: 'Upcoming releases', link: '/' },
      { label: 'Previous releases', link: '/' },
      { label: 'Releases schedule', link: '/' },
    ],
  },
  { label: 'Analytics', icon: IconPresentationAnalytics },
  { label: 'Contracts', icon: IconFileAnalytics },
  { label: 'Settings', icon: IconAdjustments },
  {
    label: 'Security',
    icon: IconLock,
    links: [
      { label: 'Enable 2FA', link: '/' },
      { label: 'Change password', link: '/' },
      { label: 'Recovery codes', link: '/' },
    ],
  },
];


export function NavbarNested() {
  const links = mockdata.map((item) => <LinksGroup {...item} key={item.label} />);


  return (
    <nav className={classes.navbar}>
      <div className={classes.header}>
        <Group justify="space-between">
          <Logo style={{ width: rem(120) }} />
          <Code fw={700}>v3.1.2</Code>
        </Group>
      </div>


      <ScrollArea className={classes.links}>
        <div className={classes.linksInner}>{links}</div>
      </ScrollArea>


      <div className={classes.footer}>
        <UserButton />
      </div>
    </nav>
  );
}
Enter fullscreen mode Exit fullscreen mode

Code: NavbarNested.module.css

.navbar {
  background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-6));
  height: rem(800px);
  width: rem(300px);
  padding: var(--mantine-spacing-md);
  padding-bottom: 0;
  display: flex;
  flex-direction: column;
  border-right: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
}


.header {
  padding: var(--mantine-spacing-md);
  padding-top: 0;
  margin-left: calc(var(--mantine-spacing-md) * -1);
  margin-right: calc(var(--mantine-spacing-md) * -1);
  color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
  border-bottom: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
}


.links {
  flex: 1;
  margin-left: calc(var(--mantine-spacing-md) * -1);
  margin-right: calc(var(--mantine-spacing-md) * -1);
}


.linksInner {
  padding-top: var(--mantine-spacing-xl);
  padding-bottom: var(--mantine-spacing-xl);
}


.footer {
  margin-left: calc(var(--mantine-spacing-md) * -1);
  margin-right: calc(var(--mantine-spacing-md) * -1);
  border-top: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
}
Enter fullscreen mode Exit fullscreen mode

• Example: Footer With Links

Image description

Code: FooterLinks.tsx

import { Text, Container, ActionIcon, Group, rem } from '@mantine/core';
import { IconBrandTwitter, IconBrandYoutube, IconBrandInstagram } from '@tabler/icons-react';
import { MantineLogo } from '@mantinex/mantine-logo';
import classes from './FooterLinks.module.css';


const data = [
  {
    title: 'About',
    links: [
      { label: 'Features', link: '#' },
      { label: 'Pricing', link: '#' },
      { label: 'Support', link: '#' },
      { label: 'Forums', link: '#' },
    ],
  },
  {
    title: 'Project',
    links: [
      { label: 'Contribute', link: '#' },
      { label: 'Media assets', link: '#' },
      { label: 'Changelog', link: '#' },
      { label: 'Releases', link: '#' },
    ],
  },
  {
    title: 'Community',
    links: [
      { label: 'Join Discord', link: '#' },
      { label: 'Follow on Twitter', link: '#' },
      { label: 'Email newsletter', link: '#' },
      { label: 'GitHub discussions', link: '#' },
    ],
  },
];


export function FooterLinks() {
  const groups = data.map((group) => {
    const links = group.links.map((link, index) => (
      <Text<'a'>
        key={index}
        className={classes.link}
        component="a"
        href={link.link}
        onClick={(event) => event.preventDefault()}
      >
        {link.label}
      </Text>
    ));


    return (
      <div className={classes.wrapper} key={group.title}>
        <Text className={classes.title}>{group.title}</Text>
        {links}
      </div>
    );
  });


  return (
    <footer className={classes.footer}>
      <Container className={classes.inner}>
        <div className={classes.logo}>
          <MantineLogo size={30} />
          <Text size="xs" c="dimmed" className={classes.description}>
            Build fully functional accessible web applications faster than ever
          </Text>
        </div>
        <div className={classes.groups}>{groups}</div>
      </Container>
      <Container className={classes.afterFooter}>
        <Text c="dimmed" size="sm">
          © 2020 mantine.dev. All rights reserved.
        </Text>


        <Group gap={0} className={classes.social} justify="flex-end" wrap="nowrap">
          <ActionIcon size="lg" color="gray" variant="subtle">
            <IconBrandTwitter style={{ width: rem(18), height: rem(18) }} stroke={1.5} />
          </ActionIcon>
          <ActionIcon size="lg" color="gray" variant="subtle">
            <IconBrandYoutube style={{ width: rem(18), height: rem(18) }} stroke={1.5} />
          </ActionIcon>
          <ActionIcon size="lg" color="gray" variant="subtle">
            <IconBrandInstagram style={{ width: rem(18), height: rem(18) }} stroke={1.5} />
          </ActionIcon>
        </Group>
      </Container>
    </footer>
  );
}
Enter fullscreen mode Exit fullscreen mode

Code: FooterLinks.module.css

.footer {
  margin-top: rem(120px);
  padding-top: calc(var(--mantine-spacing-xl) * 2);
  padding-bottom: calc(var(--mantine-spacing-xl) * 2);
  background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
  border-top: rem(1px) solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
}


.logo {
  max-width: rem(200px);


  @media (max-width: $mantine-breakpoint-sm) {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
}


.description {
  margin-top: rem(5px);


  @media (max-width: $mantine-breakpoint-sm) {
    margin-top: var(--mantine-spacing-xs);
    text-align: center;
  }
}


.inner {
  display: flex;
  justify-content: space-between;


  @media (max-width: $mantine-breakpoint-sm) {
    flex-direction: column;
    align-items: center;
  }
}


.groups {
  display: flex;
  flex-wrap: wrap;


  @media (max-width: $mantine-breakpoint-sm) {
    display: none;
  }
}


.wrapper {
  width: rem(160px);
}


.link {
  display: block;
  color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-1));
  font-size: var(--mantine-font-size-sm);
  padding-top: rem(3px);
  padding-bottom: rem(3px);


  &:hover {
    text-decoration: underline;
  }
}


.title {
  font-size: var(--mantine-font-size-lg);
  font-weight: 700;
  font-family:
    Greycliff CF,
    var(--mantine-font-family);
  margin-bottom: calc(var(--mantine-spacing-xs) / 2);
  color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
}


.afterFooter {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: var(--mantine-spacing-xl);
  padding-top: var(--mantine-spacing-xl);
  padding-bottom: var(--mantine-spacing-xl);
  border-top: rem(1px) solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4));


  @media (max-width: $mantine-breakpoint-sm) {
    flex-direction: column;
  }
}


.social {
  @media (max-width: $mantine-breakpoint-sm) {
    margin-top: var(--mantine-spacing-xs);
  }
}
Enter fullscreen mode Exit fullscreen mode

• Example: Carousel With Cards

Carousel With Cards Image

Code: CardsCarousel.tsx

import { Carousel } from '@mantine/carousel';
import { useMediaQuery } from '@mantine/hooks';
import { Paper, Text, Title, Button, useMantineTheme, rem } from '@mantine/core';
import classes from './CardsCarousel.module.css';


interface CardProps {
  image: string;
  title: string;
  category: string;
}


function Card({ image, title, category }: CardProps) {
  return (
    <Paper
      shadow="md"
      p="xl"
      radius="md"
      style={{ backgroundImage: `url(${image})` }}
      className={classes.card}
    >
      <div>
        <Text className={classes.category} size="xs">
          {category}
        </Text>
        <Title order={3} className={classes.title}>
          {title}
        </Title>
      </div>
      <Button variant="white" color="dark">
        Read article
      </Button>
    </Paper>
  );
}


const data = [
  {
    image:
      'https://images.unsplash.com/photo-1508193638397-1c4234db14d8?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=400&q=80',
    title: 'Best forests to visit in North America',
    category: 'nature',
  },
  {
    image:
      'https://images.unsplash.com/photo-1559494007-9f5847c49d94?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=400&q=80',
    title: 'Hawaii beaches review: better than you think',
    category: 'beach',
  },
  {
    image:
      'https://images.unsplash.com/photo-1608481337062-4093bf3ed404?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=400&q=80',
    title: 'Mountains at night: 12 best locations to enjoy the view',
    category: 'nature',
  },
  {
    image:
      'https://images.unsplash.com/photo-1507272931001-fc06c17e4f43?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=400&q=80',
    title: 'Aurora in Norway: when to visit for best experience',
    category: 'nature',
  },
  {
    image:
      'https://images.unsplash.com/photo-1510798831971-661eb04b3739?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=400&q=80',
    title: 'Best places to visit this winter',
    category: 'tourism',
  },
  {
    image:
      'https://images.unsplash.com/photo-1582721478779-0ae163c05a60?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=400&q=80',
    title: 'Active volcanos reviews: travel at your own risk',
    category: 'nature',
  },
];


export function CardsCarousel() {
  const theme = useMantineTheme();
  const mobile = useMediaQuery(`(max-width: ${theme.breakpoints.sm})`);
  const slides = data.map((item) => (
    <Carousel.Slide key={item.title}>
      <Card {...item} />
    </Carousel.Slide>
  ));


  return (
    <Carousel
      slideSize={{ base: '100%', sm: '50%' }}
      slideGap={{ base: rem(2), sm: 'xl' }}
      align="start"
      slidesToScroll={mobile ? 1 : 2}
    >
      {slides}
    </Carousel>
  );
}
Enter fullscreen mode Exit fullscreen mode

Code: CardsCarousel.module.css

.card {
  height: rem(440px);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-start;
  background-size: cover;
  background-position: center;
}


.title {
  font-family:
    Greycliff CF,
    sans-serif;
  font-weight: 900;
  color: var(--mantine-color-white);
  line-height: 1.2;
  font-size: rem(32px);
  margin-top: var(--mantine-spacing-xs);
}


.category {
  color: var(--mantine-color-white);
  opacity: 0.7;
  font-weight: 700;
  text-transform: uppercase;
}
Enter fullscreen mode Exit fullscreen mode

2. Page Sections

Page sections allow you to create distinct, structured parts of a webpage such as headers, footers, and hero sections.

• Example: Hero Section With Background Image

Hero Section Image

Code: HeroContent.tsx

import { Overlay, Container, Title, Button, Text } from '@mantine/core';
import classes from './HeroContentLeft.module.css';


export function HeroContentLeft() {
  return (
    <div className={classes.hero}>
      <Overlay
        gradient="linear-gradient(180deg, rgba(0, 0, 0, 0.25) 0%, rgba(0, 0, 0, .65) 40%)"
        opacity={1}
        zIndex={0}
      />
      <Container className={classes.container} size="md">
        <Title className={classes.title}>A fully featured React components library</Title>
        <Text className={classes.description} size="xl" mt="xl">
          Build fully functional accessible web applications faster than ever – Mantine includes
          more than 120 customizable components and hooks to cover you in any situation
        </Text>


        <Button variant="gradient" size="xl" radius="xl" className={classes.control}>
          Get started
        </Button>
      </Container>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Code: HeroContent.module.css

.hero {
  position: relative;
  background-image: url(https://images.unsplash.com/photo-1519389950473-47ba0277781c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80);
  background-size: cover;
  background-position: center;
}


.container {
  height: rem(700px);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: flex-start;
  padding-bottom: calc(var(--mantine-spacing-xl) * 6);
  z-index: 1;
  position: relative;


  @media (max-width: $mantine-breakpoint-sm) {
    height: rem(500px);
    padding-bottom: calc(var(--mantine-spacing-xl) * 3);
  }
}


.title {
  color: var(--mantine-color-white);
  font-size: rem(60px);
  font-weight: 900;
  line-height: 1.1;


  @media (max-width: $mantine-breakpoint-sm) {
    font-size: rem(40px);
    line-height: 1.2;
  }


  @media (max-width: $mantine-breakpoint-xs) {
    font-size: rem(28px);
    line-height: 1.3;
  }
}


.description {
  color: var(--mantine-color-white);
  max-width: rem(600px);


  @media (max-width: $mantine-breakpoint-sm) {
    max-width: 100%;
    font-size: var(--mantine-font-size-sm);
  }
}


.control {
  margin-top: calc(var(--mantine-spacing-xl) * 1.5);


  @media (max-width: $mantine-breakpoint-sm) {
    width: 100%;
  }
}
Enter fullscreen mode Exit fullscreen mode

• Example: Authentication Form

Authentication Form Image

Code: AuthForm.tsx

import { useToggle, upperFirst } from '@mantine/hooks';
import { useForm } from '@mantine/form';
import {
  TextInput,
  PasswordInput,
  Text,
  Paper,
  Group,
  PaperProps,
  Button,
  Divider,
  Checkbox,
  Anchor,
  Stack,
} from '@mantine/core';
import { GoogleButton } from './GoogleButton';
import { TwitterButton } from './TwitterButton';


export function AuthenticationForm(props: PaperProps) {
  const [type, toggle] = useToggle(['login', 'register']);
  const form = useForm({
    initialValues: {
      email: '',
      name: '',
      password: '',
      terms: true,
    },


    validate: {
      email: (val) => (/^\S+@\S+$/.test(val) ? null : 'Invalid email'),
      password: (val) => (val.length <= 6 ? 'Password should include at least 6 characters' : null),
    },
  });


  return (
    <Paper radius="md" p="xl" withBorder {...props}>
      <Text size="lg" fw={500}>
        Welcome to Mantine, {type} with
      </Text>


      <Group grow mb="md" mt="md">
        <GoogleButton radius="xl">Google</GoogleButton>
        <TwitterButton radius="xl">Twitter</TwitterButton>
      </Group>


      <Divider label="Or continue with email" labelPosition="center" my="lg" />


      <form onSubmit={form.onSubmit(() => {})}>
        <Stack>
          {type === 'register' && (
            <TextInput
              label="Name"
              placeholder="Your name"
              value={form.values.name}
              onChange={(event) => form.setFieldValue('name', event.currentTarget.value)}
              radius="md"
            />
          )}


          <TextInput
            required
            label="Email"
            placeholder="hello@mantine.dev"
            value={form.values.email}
            onChange={(event) => form.setFieldValue('email', event.currentTarget.value)}
            error={form.errors.email && 'Invalid email'}
            radius="md"
          />


          <PasswordInput
            required
            label="Password"
            placeholder="Your password"
            value={form.values.password}
            onChange={(event) => form.setFieldValue('password', event.currentTarget.value)}
            error={form.errors.password && 'Password should include at least 6 characters'}
            radius="md"
          />


          {type === 'register' && (
            <Checkbox
              label="I accept terms and conditions"
              checked={form.values.terms}
              onChange={(event) => form.setFieldValue('terms', event.currentTarget.checked)}
            />
          )}
        </Stack>


        <Group justify="space-between" mt="xl">
          <Anchor component="button" type="button" c="dimmed" onClick={() => toggle()} size="xs">
            {type === 'register'
              ? 'Already have an account? Login'
              : "Don't have an account? Register"}
          </Anchor>
          <Button type="submit" radius="xl">
            {upperFirst(type)}
          </Button>
        </Group>
      </form>
    </Paper>
  );
}
Enter fullscreen mode Exit fullscreen mode

Code: GoogleButton.tsx

import { Button, ButtonProps } from '@mantine/core';

function GoogleIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      preserveAspectRatio="xMidYMid"
      viewBox="0 0 256 262"
      style={{ width: '0.9rem', height: '0.9rem' }}
      {...props}
    >
      <path
        fill="#4285F4"
        d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622 38.755 30.023 2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"
      />
      <path
        fill="#34A853"
        d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055-34.523 0-63.824-22.773-74.269-54.25l-1.531.13-40.298 31.187-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"
      />
      <path
        fill="#FBBC05"
        d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82 0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602l42.356-32.782"
      />
      <path
        fill="#EB4335"
        d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0 79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"
      />
    </svg>
  );
}

export function GoogleButton(props: ButtonProps & React.ComponentPropsWithoutRef<'button'>) {
  return <Button leftSection={<GoogleIcon />} variant="default" {...props} />;
}
Enter fullscreen mode Exit fullscreen mode

Code: TwitterButton.tsx

import { Button, ButtonProps } from '@mantine/core';
import { TwitterIcon } from '@mantinex/dev-icons';


export function TwitterButton(props: ButtonProps & React.ComponentPropsWithoutRef<'button'>) {
  return (
    <Button
      leftSection={<TwitterIcon style={{ width: '1rem', height: '1rem' }} color="#00ACEE" />}
      variant="default"
      {...props}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

• Example: 404 Page

404 Page Image

Code: NotFoundBackground.tsx

import { Container, Title, Text, Button, Group } from '@mantine/core';
import { Illustration } from './Illustration';
import classes from './NothingFoundBackground.module.css';


export function NothingFoundBackground() {
  return (
    <Container className={classes.root}>
      <div className={classes.inner}>
        <Illustration className={classes.image} />
        <div className={classes.content}>
          <Title className={classes.title}>Nothing to see here</Title>
          <Text c="dimmed" size="lg" ta="center" className={classes.description}>
            Page you are trying to open does not exist. You may have mistyped the address, or the
            page has been moved to another URL. If you think this is an error contact support.
          </Text>
          <Group justify="center">
            <Button size="md">Take me back to home page</Button>
          </Group>
        </div>
      </div>
    </Container>
  );
}
Enter fullscreen mode Exit fullscreen mode

Code: Illustration.tsx

export function Illustration(props: React.ComponentPropsWithoutRef<'svg'>) {
  return (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 362 145" {...props}>
      <path
        fill="currentColor"
        d="M62.6 142c-2.133 0-3.2-1.067-3.2-3.2V118h-56c-2 0-3-1-3-3V92.8c0-1.333.4-2.733 1.2-4.2L58.2 4c.8-1.333 2.067-2 3.8-2h28c2 0 3 1 3 3v85.4h11.2c.933 0 1.733.333 2.4 1 .667.533 1 1.267 1 2.2v21.2c0 .933-.333 1.733-1 2.4-.667.533-1.467.8-2.4.8H93v20.8c0 2.133-1.067 3.2-3.2 3.2H62.6zM33 90.4h26.4V51.2L33 90.4zM181.67 144.6c-7.333 0-14.333-1.333-21-4-6.666-2.667-12.866-6.733-18.6-12.2-5.733-5.467-10.266-13-13.6-22.6-3.333-9.6-5-20.667-5-33.2 0-12.533 1.667-23.6 5-33.2 3.334-9.6 7.867-17.133 13.6-22.6 5.734-5.467 11.934-9.533 18.6-12.2 6.667-2.8 13.667-4.2 21-4.2 7.467 0 14.534 1.4 21.2 4.2 6.667 2.667 12.8 6.733 18.4 12.2 5.734 5.467 10.267 13 13.6 22.6 3.334 9.6 5 20.667 5 33.2 0 12.533-1.666 23.6-5 33.2-3.333 9.6-7.866 17.133-13.6 22.6-5.6 5.467-11.733 9.533-18.4 12.2-6.666 2.667-13.733 4-21.2 4zm0-31c9.067 0 15.6-3.733 19.6-11.2 4.134-7.6 6.2-17.533 6.2-29.8s-2.066-22.2-6.2-29.8c-4.133-7.6-10.666-11.4-19.6-11.4-8.933 0-15.466 3.8-19.6 11.4-4 7.6-6 17.533-6 29.8s2 22.2 6 29.8c4.134 7.467 10.667 11.2 19.6 11.2zM316.116 142c-2.134 0-3.2-1.067-3.2-3.2V118h-56c-2 0-3-1-3-3V92.8c0-1.333.4-2.733 1.2-4.2l56.6-84.6c.8-1.333 2.066-2 3.8-2h28c2 0 3 1 3 3v85.4h11.2c.933 0 1.733.333 2.4 1 .666.533 1 1.267 1 2.2v21.2c0 .933-.334 1.733-1 2.4-.667.533-1.467.8-2.4.8h-11.2v20.8c0 2.133-1.067 3.2-3.2 3.2h-27.2zm-29.6-51.6h26.4V51.2l-26.4 39.2z"
      />
    </svg>
  );
}
Enter fullscreen mode Exit fullscreen mode

Code: NotFoundBackground.module.css

.root {
  padding-top: rem(80px);
  padding-bottom: rem(80px);
}


.inner {
  position: relative;
}


.image {
  position: absolute;
  inset: 0;
  opacity: 0.75;
  color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
}


.content {
  padding-top: rem(220px);
  position: relative;
  z-index: 1;


  @media (max-width: $mantine-breakpoint-sm) {
    padding-top: rem(120px);
  }
}


.title {
  font-family:
    Greycliff CF,
    var(--mantine-font-family);
  text-align: center;
  font-weight: 900;
  font-size: rem(38px);


  @media (max-width: $mantine-breakpoint-sm) {
    font-size: rem(32px);
  }
}


.description {
  max-width: rem(540px);
  margin: auto;
  margin-top: var(--mantine-spacing-xl);
  margin-bottom: calc(var(--mantine-spacing-xl) * 1.5);
}
Enter fullscreen mode Exit fullscreen mode

3. Blog UI

Blog UI components are designed to enhance the readability and structure of blog content. These components focus on making your blog visually appealing while maintaining a smooth user experience.

• Example: Article Cards

Article Cards Image

Code: ArticlesCardsGrid.tsx

import { SimpleGrid, Card, Image, Text, Container, AspectRatio } from '@mantine/core';
import classes from './ArticlesCardsGrid.module.css';

const mockdata = [
  {
    title: 'Top 10 places to visit in Norway this summer',
    image:
      'https://images.unsplash.com/photo-1527004013197-933c4bb611b3?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=720&q=80',
    date: 'August 18, 2022',
  },
  {
    title: 'Best forests to visit in North America',
    image:
      'https://images.unsplash.com/photo-1448375240586-882707db888b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=720&q=80',
    date: 'August 27, 2022',
  },
  {
    title: 'Hawaii beaches review: better than you think',
    image:
      'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=720&q=80',
    date: 'September 9, 2022',
  },
  {
    title: 'Mountains at night: 12 best locations to enjoy the view',
    image:
      'https://images.unsplash.com/photo-1519681393784-d120267933ba?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=720&q=80',
    date: 'September 12, 2022',
  },
];

export function ArticlesCardsGrid() {
  const cards = mockdata.map((article) => (
    <Card key={article.title} p="md" radius="md" component="a" href="#" className={classes.card}>
      <AspectRatio ratio={1920 / 1080}>
        <Image src={article.image} />
      </AspectRatio>
      <Text c="dimmed" size="xs" tt="uppercase" fw={700} mt="md">
        {article.date}
      </Text>
      <Text className={classes.title} mt={5}>
        {article.title}
      </Text>
    </Card>
  ));

  return (
    <Container py="xl">
      <SimpleGrid cols={{ base: 1, sm: 2 }}>{cards}</SimpleGrid>
    </Container>
  );
}
Enter fullscreen mode Exit fullscreen mode

Code: ArticlesCardsGrid.module.css

.card {
  transition:
    transform 150ms ease,
    box-shadow 150ms ease;


  &:hover {
    transform: scale(1.01);
    box-shadow: var(--mantine-shadow-md);
  }
}


.title {
  font-family: 'Greycliff CF', var(--mantine-font-family);
  font-weight: bold;
}
Enter fullscreen mode Exit fullscreen mode

• Example: Table of Contents

Table of Contents Image

Code: TableOfContents.tsx

import cx from 'clsx';
import { Box, Text, Group, rem } from '@mantine/core';
import { IconListSearch } from '@tabler/icons-react';
import classes from './TableOfContents.module.css';


const links = [
  { label: 'Usage', link: '#usage', order: 1 },
  { label: 'Position and placement', link: '#position', order: 1 },
  { label: 'With other overlays', link: '#overlays', order: 1 },
  { label: 'Manage focus', link: '#focus', order: 1 },
  { label: 'Examples', link: '#1', order: 1 },
  { label: 'Show on focus', link: '#2', order: 2 },
  { label: 'Show on hover', link: '#3', order: 2 },
  { label: 'With form', link: '#4', order: 2 },
];


const active = '#overlays';


export function TableOfContents() {
  const items = links.map((item) => (
    <Box<'a'>
      component="a"
      href={item.link}
      onClick={(event) => event.preventDefault()}
      key={item.label}
      className={cx(classes.link, { [classes.linkActive]: active === item.link })}
      style={{ paddingLeft: `calc(${item.order} * var(--mantine-spacing-md))` }}
    >
      {item.label}
    </Box>
  ));


  return (
    <div>
      <Group mb="md">
        <IconListSearch style={{ width: rem(18), height: rem(18) }} stroke={1.5} />
        <Text>Table of contents</Text>
      </Group>
      {items}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Code: TableOfContents.module.css

.link {
  display: block;
  text-decoration: none;
  color: var(--mantine-color-text);
  line-height: 1.2;
  font-size: var(--mantine-font-size-sm);
  padding: var(--mantine-spacing-xs);
  border-top-right-radius: var(--mantine-radius-sm);
  border-bottom-right-radius: var(--mantine-radius-sm);
  border-left: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));


  @mixin hover {
    background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
  }
}


.linkActive {
  font-weight: 500;
  border-left-color: light-dark(var(--mantine-color-blue-6), var(--mantine-color-blue-4));
  color: light-dark(var(--mantine-color-blue-6), var(--mantine-color-blue-4));


  &,
  &:hover {
    background-color: var(--mantine-color-blue-light) !important;
  }
}
Enter fullscreen mode Exit fullscreen mode

• Example: Comment With HTML Links

Table of Contents Image

Code: CommentHtml.tsx

import { Text, Avatar, Group, TypographyStylesProvider, Paper } from '@mantine/core';
import classes from './CommentHtml.module.css';

export function CommentHtml() {
  return (
    <Paper withBorder radius="md" className={classes.comment}>
      <Group>
        <Avatar
          src="https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-2.png"
          alt="Jacob Warnhalter"
          radius="xl"
        />
        <div>
          <Text fz="sm">Jacob Warnhalter</Text>
          <Text fz="xs" c="dimmed">
            10 minutes ago
          </Text>
        </div>
      </Group>
      <TypographyStylesProvider className={classes.body}>
        <div
          className={classes.content}
          dangerouslySetInnerHTML={{
            __html:
              '<p>I use <a href="https://heroku.com/" rel="noopener noreferrer" target="_blank">Heroku</a> to host my Node.js application, but MongoDB add-on appears to be too <strong>expensive</strong>. I consider switching to <a href="https://www.digitalocean.com/" rel="noopener noreferrer" target="_blank">Digital Ocean</a> VPS to save some cash.</p>',
          }}
        />
      </TypographyStylesProvider>
    </Paper>
  );
}
Enter fullscreen mode Exit fullscreen mode

Code: CommentHtml.module.css

.comment {
  padding: var(--mantine-spacing-lg) var(--mantine-spacing-xl);
}


.body {
  padding-left: rem(54px);
  padding-top: var(--mantine-spacing-sm);
  font-size: var(--mantine-font-size-sm);
}


.content {
  & > p:last-child {
    margin-bottom: 0;
  }
}
Enter fullscreen mode Exit fullscreen mode

Mantine UI vs Other Component Libraries: Why Choose Mantine?

When comparing Mantine UI to other popular libraries such as Material UI, Chakra UI, and Bootstrap, each has its strengths. Here’s a straightforward comparison to help you decide which fits your project best:

  1. Mantine UI vs Material UI

    • Customization: Mantine UI offers extensive customization options with its sx prop, allowing easy changes to styles. Material UI follows strict Material Design guidelines, which ensures consistency but can limit flexibility.
    • Performance: Mantine UI is known for its smaller bundle sizes, potentially improving performance in large applications. Material UI, while feature-rich, can result in larger bundle sizes.
  2. Mantine UI vs Chakra UI

    • Component Variety: Mantine UI has a larger number of pre-built components, covering more advanced UI elements such as notifications and modals. Chakra UI focuses on simplicity and ease of use with a more minimal set of components.
    • Responsiveness: Both libraries are highly responsive, but Mantine UI includes light and dark mode support for most components, while Chakra UI offers similar functionality but may require additional configuration.
  3. Mantine UI vs Bootstrap

    • Design Flexibility: Mantine UI offers a more modern design with more flexible customization options. Bootstrap follows a grid-based, utility-first design system, which is more opinionated but widely adopted for traditional layouts.
    • Component Modernity: Mantine UI includes more modern, React-focused components like modals, notifications, and rich text editors, while Bootstrap is primarily focused on general-purpose UI components, suitable for both static and dynamic websites.

Conclusion

Mantine UI offers a flexible and modern solution for building responsive web applications with its extensive component library, easy customization, and built-in support for theming. Compared to other popular libraries like Material UI, Chakra UI, and Bootstrap, Mantine stands out for its smaller bundle sizes, broader component variety, and more developer-friendly styling options. However, each library has its strengths, and the choice ultimately depends on your project’s specific requirements for design flexibility, performance, and ease of use.

Top comments (0)