Claude Code Plugins

Community-maintained marketplace

Feedback

Builds React applications with Mantine's 100+ components, hooks, and form library. Use when creating feature-rich UIs with built-in dark mode, accessibility, and TypeScript support.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name mantine
description Builds React applications with Mantine's 100+ components, hooks, and form library. Use when creating feature-rich UIs with built-in dark mode, accessibility, and TypeScript support.

Mantine

Full-featured React component library with 100+ customizable components and 50+ hooks.

Quick Start

npm install @mantine/core @mantine/hooks
npm install -D postcss postcss-preset-mantine postcss-simple-vars
// postcss.config.cjs
module.exports = {
  plugins: {
    'postcss-preset-mantine': {},
    'postcss-simple-vars': {
      variables: {
        'mantine-breakpoint-xs': '36em',
        'mantine-breakpoint-sm': '48em',
        'mantine-breakpoint-md': '62em',
        'mantine-breakpoint-lg': '75em',
        'mantine-breakpoint-xl': '88em',
      },
    },
  },
};
// App.tsx
import '@mantine/core/styles.css';
import { MantineProvider, createTheme } from '@mantine/core';

const theme = createTheme({
  primaryColor: 'blue',
  fontFamily: 'Inter, sans-serif',
});

function App() {
  return (
    <MantineProvider theme={theme}>
      <YourApp />
    </MantineProvider>
  );
}
// Usage
import { Button, TextInput, Group, Stack } from '@mantine/core';

function Demo() {
  return (
    <Stack>
      <TextInput label="Email" placeholder="you@example.com" />
      <Group>
        <Button>Submit</Button>
        <Button variant="outline">Cancel</Button>
      </Group>
    </Stack>
  );
}

Core Components

Button

import { Button, ActionIcon } from '@mantine/core';
import { IconSettings } from '@tabler/icons-react';

// Variants
<Button variant="filled">Filled</Button>
<Button variant="light">Light</Button>
<Button variant="outline">Outline</Button>
<Button variant="subtle">Subtle</Button>
<Button variant="transparent">Transparent</Button>
<Button variant="white">White</Button>

// Sizes
<Button size="xs">Extra Small</Button>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
<Button size="xl">Extra Large</Button>

// Colors
<Button color="blue">Blue</Button>
<Button color="red">Red</Button>
<Button color="green">Green</Button>

// With icons
<Button leftSection={<IconSettings size={16} />}>Settings</Button>
<Button rightSection={<IconArrow size={16} />}>Next</Button>

// Loading
<Button loading>Processing...</Button>

// Icon button
<ActionIcon variant="filled" size="lg">
  <IconSettings size={18} />
</ActionIcon>

TextInput

import { TextInput, PasswordInput, Textarea, NumberInput } from '@mantine/core';

<TextInput
  label="Email"
  description="We'll never share your email"
  placeholder="you@example.com"
  required
  error="Invalid email"
/>

<PasswordInput
  label="Password"
  placeholder="Enter password"
  visible={visible}
  onVisibilityChange={toggle}
/>

<Textarea
  label="Message"
  placeholder="Your message"
  autosize
  minRows={3}
  maxRows={6}
/>

<NumberInput
  label="Amount"
  placeholder="Enter amount"
  min={0}
  max={100}
  step={5}
  prefix="$"
  thousandSeparator=","
/>

Select and MultiSelect

import { Select, MultiSelect, Combobox } from '@mantine/core';

<Select
  label="Framework"
  placeholder="Pick one"
  data={['React', 'Vue', 'Angular', 'Svelte']}
  searchable
  clearable
/>

<Select
  label="User"
  data={[
    { value: '1', label: 'John Doe' },
    { value: '2', label: 'Jane Smith' },
  ]}
/>

<MultiSelect
  label="Technologies"
  placeholder="Select technologies"
  data={['TypeScript', 'JavaScript', 'Python', 'Rust']}
  searchable
  maxSelectedValues={3}
/>

Modal and Drawer

import { Modal, Button } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function ModalDemo() {
  const [opened, { open, close }] = useDisclosure(false);

  return (
    <>
      <Modal opened={opened} onClose={close} title="Authentication">
        Modal content here...
      </Modal>
      <Button onClick={open}>Open modal</Button>
    </>
  );
}

// Drawer
import { Drawer } from '@mantine/core';

<Drawer
  opened={opened}
  onClose={close}
  title="Navigation"
  position="left"  // left, right, top, bottom
  size="md"
>
  Drawer content...
</Drawer>

Notifications

npm install @mantine/notifications
import '@mantine/notifications/styles.css';
import { Notifications, notifications } from '@mantine/notifications';

// Add to app root
<MantineProvider>
  <Notifications position="top-right" />
  <App />
</MantineProvider>

// Show notification
notifications.show({
  title: 'Success',
  message: 'Your changes have been saved',
  color: 'green',
  autoClose: 5000,
});

// Update existing notification
const id = notifications.show({
  loading: true,
  title: 'Loading...',
  message: 'Please wait',
  autoClose: false,
});

notifications.update({
  id,
  loading: false,
  title: 'Complete',
  message: 'Data loaded successfully',
  color: 'green',
  autoClose: 3000,
});

Tabs

import { Tabs } from '@mantine/core';

<Tabs defaultValue="gallery">
  <Tabs.List>
    <Tabs.Tab value="gallery">Gallery</Tabs.Tab>
    <Tabs.Tab value="messages">Messages</Tabs.Tab>
    <Tabs.Tab value="settings">Settings</Tabs.Tab>
  </Tabs.List>

  <Tabs.Panel value="gallery" pt="md">
    Gallery content
  </Tabs.Panel>
  <Tabs.Panel value="messages" pt="md">
    Messages content
  </Tabs.Panel>
  <Tabs.Panel value="settings" pt="md">
    Settings content
  </Tabs.Panel>
</Tabs>

Card

import { Card, Image, Text, Badge, Button, Group } from '@mantine/core';

<Card shadow="sm" padding="lg" radius="md" withBorder>
  <Card.Section>
    <Image src="/product.jpg" height={160} alt="Product" />
  </Card.Section>

  <Group justify="space-between" mt="md" mb="xs">
    <Text fw={500}>Product Name</Text>
    <Badge color="pink">On Sale</Badge>
  </Group>

  <Text size="sm" c="dimmed">
    Product description goes here.
  </Text>

  <Button fullWidth mt="md" radius="md">
    Add to Cart
  </Button>
</Card>

Forms

npm install @mantine/form
import { useForm } from '@mantine/form';
import { TextInput, Button, Stack } from '@mantine/core';

function ContactForm() {
  const form = useForm({
    mode: 'uncontrolled',
    initialValues: {
      name: '',
      email: '',
      message: '',
    },
    validate: {
      name: (value) => value.length < 2 ? 'Name is too short' : null,
      email: (value) => /^\S+@\S+$/.test(value) ? null : 'Invalid email',
      message: (value) => value.length < 10 ? 'Message too short' : null,
    },
  });

  return (
    <form onSubmit={form.onSubmit((values) => console.log(values))}>
      <Stack>
        <TextInput
          label="Name"
          placeholder="Your name"
          key={form.key('name')}
          {...form.getInputProps('name')}
        />
        <TextInput
          label="Email"
          placeholder="your@email.com"
          key={form.key('email')}
          {...form.getInputProps('email')}
        />
        <Textarea
          label="Message"
          placeholder="Your message"
          key={form.key('message')}
          {...form.getInputProps('message')}
        />
        <Button type="submit">Submit</Button>
      </Stack>
    </form>
  );
}

Form with Zod Validation

import { useForm, zodResolver } from '@mantine/form';
import { z } from 'zod';

const schema = z.object({
  name: z.string().min(2, 'Name must be at least 2 characters'),
  email: z.string().email('Invalid email'),
  age: z.number().min(18, 'Must be 18 or older'),
});

const form = useForm({
  mode: 'uncontrolled',
  initialValues: { name: '', email: '', age: 18 },
  validate: zodResolver(schema),
});

Hooks

import {
  useDisclosure,
  useToggle,
  useDebouncedValue,
  useClipboard,
  useLocalStorage,
  useMediaQuery,
  useClickOutside,
  useHotkeys,
  useForm,
} from '@mantine/hooks';

// Disclosure (modals, menus)
const [opened, { open, close, toggle }] = useDisclosure(false);

// Toggle between values
const [value, toggle] = useToggle(['light', 'dark']);

// Debounced value
const [search, setSearch] = useState('');
const [debounced] = useDebouncedValue(search, 300);

// Clipboard
const clipboard = useClipboard({ timeout: 2000 });
clipboard.copy('Text to copy');
clipboard.copied // true after copy

// Local storage
const [value, setValue] = useLocalStorage({
  key: 'user-preference',
  defaultValue: 'dark',
});

// Media query
const isMobile = useMediaQuery('(max-width: 768px)');

// Click outside
const ref = useClickOutside(() => setOpened(false));

// Keyboard shortcuts
useHotkeys([
  ['mod+K', () => openSearch()],
  ['mod+S', () => saveDocument()],
  ['Escape', () => closeModal()],
]);

Theming

Custom Theme

import { createTheme, MantineProvider } from '@mantine/core';

const theme = createTheme({
  // Primary color
  primaryColor: 'brand',
  primaryShade: { light: 6, dark: 8 },

  // Custom colors (10 shades each)
  colors: {
    brand: [
      '#e6f2ff', '#b3d9ff', '#80bfff', '#4da6ff', '#1a8cff',
      '#0073e6', '#005cb3', '#004480', '#002d4d', '#00161a',
    ],
  },

  // Typography
  fontFamily: 'Inter, sans-serif',
  fontFamilyMonospace: 'Fira Code, monospace',
  headings: {
    fontFamily: 'Inter, sans-serif',
    fontWeight: '600',
    sizes: {
      h1: { fontSize: '2.5rem', lineHeight: '1.2' },
      h2: { fontSize: '2rem', lineHeight: '1.3' },
    },
  },

  // Spacing scale
  spacing: {
    xs: '0.5rem',
    sm: '0.75rem',
    md: '1rem',
    lg: '1.5rem',
    xl: '2rem',
  },

  // Border radius
  radius: {
    xs: '2px',
    sm: '4px',
    md: '8px',
    lg: '16px',
    xl: '32px',
  },
  defaultRadius: 'md',

  // Shadows
  shadows: {
    xs: '0 1px 2px rgba(0, 0, 0, 0.05)',
    sm: '0 1px 3px rgba(0, 0, 0, 0.1)',
    md: '0 4px 6px rgba(0, 0, 0, 0.1)',
    lg: '0 10px 15px rgba(0, 0, 0, 0.1)',
    xl: '0 20px 25px rgba(0, 0, 0, 0.15)',
  },

  // Focus styles
  focusRing: 'auto', // 'auto' | 'always' | 'never'

  // Other
  cursorType: 'pointer', // 'default' | 'pointer'
  autoContrast: true,
});

Dark Mode

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

// Provider setup
<MantineProvider defaultColorScheme="auto">
  <App />
</MantineProvider>

// Toggle button
function ColorSchemeToggle() {
  const { colorScheme, toggleColorScheme } = useMantineColorScheme();

  return (
    <Button onClick={() => toggleColorScheme()}>
      {colorScheme === 'dark' ? 'Light' : 'Dark'} mode
    </Button>
  );
}

Styles API

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

// Using classNames prop
<Button
  classNames={{
    root: 'my-button-root',
    label: 'my-button-label',
  }}
>
  Styled Button
</Button>

// Using styles prop
<Button
  styles={{
    root: { backgroundColor: 'red' },
    label: { color: 'white' },
  }}
>
  Inline Styled
</Button>

// CSS Modules (recommended)
// Button.module.css
.root {
  background-color: var(--mantine-color-blue-6);
}

.label {
  font-weight: 700;
}

// Component
import classes from './Button.module.css';
<Button classNames={classes}>CSS Modules</Button>

CSS Variables

// Access theme tokens via CSS variables
.myComponent {
  background-color: var(--mantine-color-blue-6);
  padding: var(--mantine-spacing-md);
  border-radius: var(--mantine-radius-md);
  font-size: var(--mantine-font-size-sm);

  /* Dark mode specific */
  @mixin dark {
    background-color: var(--mantine-color-dark-6);
  }

  /* Responsive */
  @media (max-width: $mantine-breakpoint-sm) {
    padding: var(--mantine-spacing-xs);
  }
}

Layout

import {
  AppShell,
  Container,
  Grid,
  Group,
  Stack,
  Flex,
  SimpleGrid,
  Center,
} from '@mantine/core';

// App shell layout
<AppShell
  header={{ height: 60 }}
  navbar={{ width: 300, breakpoint: 'sm', collapsed: { mobile: !opened } }}
  padding="md"
>
  <AppShell.Header>Header</AppShell.Header>
  <AppShell.Navbar>Navbar</AppShell.Navbar>
  <AppShell.Main>Content</AppShell.Main>
</AppShell>

// Container with max width
<Container size="md">Content</Container>

// Grid
<Grid>
  <Grid.Col span={6}>Half</Grid.Col>
  <Grid.Col span={6}>Half</Grid.Col>
  <Grid.Col span={{ base: 12, md: 6, lg: 4 }}>Responsive</Grid.Col>
</Grid>

// Simple grid (equal columns)
<SimpleGrid cols={3}>
  <div>1</div>
  <div>2</div>
  <div>3</div>
</SimpleGrid>

// Flex layouts
<Group justify="space-between" align="center">
  <Text>Left</Text>
  <Text>Right</Text>
</Group>

<Stack gap="md">
  <Text>Item 1</Text>
  <Text>Item 2</Text>
</Stack>

<Flex gap="md" wrap="wrap" justify="center">
  <Box>Item</Box>
  <Box>Item</Box>
</Flex>

Best Practices

  1. Use CSS modules - Most performant styling approach
  2. Leverage hooks - useDisclosure, useDebouncedValue, useForm for common patterns
  3. Use Styles API - Override component parts with classNames prop
  4. Theme tokens - Use CSS variables for consistent theming
  5. Form validation - Use @mantine/form with Zod for type-safe forms

Reference Files