Claude-skill-registry-data mantine-developing

Build React UIs with Mantine component library. Customize styles with Styles API, handle forms with @mantine/form, implement theming, and avoid accessibility pitfalls. Use when creating Mantine components or fixing styling issues.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry-data
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry-data "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/mantine-developing" ~/.claude/skills/majiayu000-claude-skill-registry-data-mantine-developing && rm -rf "$T"
manifest: data/mantine-developing/SKILL.md
source content

Mantine UI Development

Purpose

Build React UIs with Mantine's component library, Styles API for customization, and @mantine/form for form handling.

When NOT to Use

  • Basic React development (Claude knows this)
  • Non-Mantine UI libraries (Material UI, Chakra, etc.)
  • Simple HTML/CSS without Mantine
  • Mantine v6 or earlier (this skill covers v7+)

Quick Start

Step 1: Import Components

// Core components
import { Button, TextInput, Select, Modal } from '@mantine/core';

// Hooks
import { useDisclosure, useToggle } from '@mantine/hooks';

// Form handling
import { useForm } from '@mantine/form';

Step 2: Basic Component Usage

<Button variant="filled" color="blue" size="md" loading={isLoading}>
  Submit
</Button>

<TextInput
  label="Email"
  placeholder="your@email.com"
  error={errors.email}
  required
/>

Step 3: Customize with Styles API

See Core Patterns below.

Step 4: Validate Accessibility

  • Add
    aria-label
    to icon-only buttons
  • Check keyboard navigation works
  • Verify focus states are visible

Core Patterns

Styles API (Critical)

Mantine components have multiple inner elements. Use Styles API to target them:

// Option 1: classNames prop (recommended for CSS modules)
import classes from './Button.module.css';

<Button classNames={{
  root: classes.button,      // Outer wrapper
  label: classes.buttonLabel // Text content
}} />
/* Button.module.css */
.button {
  background: linear-gradient(45deg, blue, purple);
}
.buttonLabel {
  font-weight: 700;
}
// Option 2: styles prop (inline styles for inner elements)
<Button styles={{
  root: { padding: '10px 20px' },
  label: { color: 'white' }
}} />

// Option 3: CSS variables
<Button style={{ '--button-bg': 'var(--mantine-color-blue-6)' }} />

// Option 4: Data attributes for state
<Button data-active={isActive} />

Finding selector names: Check Mantine docs for each component's "Styles API" section listing available selectors.

Form Handling (@mantine/form)

import { useForm } from '@mantine/form';
import { TextInput, Checkbox, Button, Box } from '@mantine/core';

function LoginForm() {
  const form = useForm({
    initialValues: {
      email: '',
      password: '',
      rememberMe: false,
    },
    validate: {
      email: (value) =>
        /^\S+@\S+$/.test(value) ? null : 'Invalid email',
      password: (value) =>
        value.length >= 8 ? null : 'Password must be 8+ characters',
    },
    // Optional: validate on change instead of only on submit
    validateInputOnChange: true,
  });

  const handleSubmit = (values: typeof form.values) => {
    console.log('Form submitted:', values);
  };

  return (
    <Box component="form" onSubmit={form.onSubmit(handleSubmit)}>
      <TextInput
        label="Email"
        placeholder="your@email.com"
        {...form.getInputProps('email')}
      />
      <TextInput
        label="Password"
        type="password"
        {...form.getInputProps('password')}
      />
      <Checkbox
        label="Remember me"
        {...form.getInputProps('rememberMe', { type: 'checkbox' })}
      />
      <Button type="submit" mt="md">Login</Button>
    </Box>
  );
}

Key form methods:

  • form.getInputProps('fieldName')
    - Binds value, onChange, error
  • form.getInputProps('field', { type: 'checkbox' })
    - For checkboxes/switches
  • form.onSubmit(handler)
    - Validates before calling handler
  • form.setFieldValue('field', value)
    - Programmatic updates
  • form.reset()
    - Reset to initial values
  • form.validate()
    - Manual validation

Polymorphic Components

Change the underlying HTML element:

// Button as link
<Button component="a" href="/dashboard">
  Go to Dashboard
</Button>

// Text as span (default is p)
<Text component="span">Inline text</Text>

// Card as article
<Card component="article">Content</Card>

TypeScript note: Ref type changes with component prop!

// Default: HTMLButtonElement
const buttonRef = useRef<HTMLButtonElement>(null);
<Button ref={buttonRef} />

// With component="a": HTMLAnchorElement
const linkRef = useRef<HTMLAnchorElement>(null);
<Button component="a" ref={linkRef} href="/" />

Theming

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

const theme = createTheme({
  primaryColor: 'violet',
  colors: {
    // Add custom color palette
    brand: [
      '#f0e6ff', '#d9c2ff', '#c29dff', '#ab79ff',
      '#9454ff', '#7d30ff', '#660bff', '#5200d9',
      '#3e00a6', '#2b0073'
    ],
  },
  components: {
    Button: {
      defaultProps: {
        radius: 'md',
      },
    },
  },
});

function App() {
  return (
    <MantineProvider theme={theme}>
      {/* Your app */}
    </MantineProvider>
  );
}

Dark Mode

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

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

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

CSS for dark mode:

/* Use light-dark() function */
.myComponent {
  background: light-dark(white, var(--mantine-color-dark-7));
  color: light-dark(black, white);
}

Common Pitfalls

1. Nested Interactive Elements

Wrong:

<Accordion.Control>
  <Button onClick={handleDelete}>Delete</Button>  {/* Invalid! */}
</Accordion.Control>

Correct:

<Accordion.Item>
  <Box style={{ display: 'flex', alignItems: 'center' }}>
    <Accordion.Control style={{ flex: 1 }}>Title</Accordion.Control>
    <Button onClick={handleDelete}>Delete</Button>
  </Box>
  <Accordion.Panel>Content</Accordion.Panel>
</Accordion.Item>

2. Tooltip on Disabled Button

Wrong:

<Tooltip label="Disabled">
  <Button disabled>Click me</Button>  {/* Tooltip won't show! */}
</Tooltip>

Correct:

<Tooltip label="Disabled">
  <Button data-disabled onClick={(e) => e.preventDefault()}>
    Click me
  </Button>
</Tooltip>

3. ActionIcon.Group Wrapping

Wrong:

<ActionIcon.Group>
  <div>  {/* Invalid wrapper! */}
    <ActionIcon><IconEdit /></ActionIcon>
  </div>
</ActionIcon.Group>

Correct:

<ActionIcon.Group>
  <ActionIcon><IconEdit /></ActionIcon>
  <ActionIcon><IconTrash /></ActionIcon>
</ActionIcon.Group>

4. Missing aria-label

Wrong:

<ActionIcon>
  <IconSearch />  {/* No accessible name! */}
</ActionIcon>

Correct:

<ActionIcon aria-label="Search">
  <IconSearch />
</ActionIcon>

5. AspectRatio in Flexbox

Wrong:

<Flex>
  <AspectRatio ratio={16/9}>  {/* Won't size correctly */}
    <Image src="/photo.jpg" />
  </AspectRatio>
</Flex>

Correct:

<Flex>
  <AspectRatio ratio={16/9} w={300}>  {/* Explicit width */}
    <Image src="/photo.jpg" />
  </AspectRatio>
</Flex>

6. Select vs Autocomplete Confusion

  • Select: User MUST choose from options (enforces list)
  • Autocomplete: User CAN type any value (suggestions only)
// Use Select when value must be from list
<Select data={['Option 1', 'Option 2']} />

// Use Autocomplete when free text is acceptable
<Autocomplete data={['Suggestion 1', 'Suggestion 2']} />

Accessibility Requirements

  1. Icon buttons must have aria-label:

    <ActionIcon aria-label="Close modal">
      <IconX />
    </ActionIcon>
    
  2. Use closeButtonLabel prop:

    <Modal closeButtonLabel="Close this dialog" opened={opened} onClose={close}>
    
  3. Set heading order for accordions:

    <Accordion.Control order={3}>  {/* Renders as <h3> */}
    
  4. Test keyboard navigation:

    • Tab through form fields
    • Enter/Space on buttons
    • Escape to close modals
    • Arrow keys in menus

Troubleshooting

ProblemCauseSolution
Styles not applyingWrong selector nameCheck component's Styles API docs for available selectors
Dark mode has wrong colorsHardcoded color valuesUse
light-dark()
CSS function or theme colors
Form not validatingWrong configUse
validate
for submit-time,
validateInputOnChange
for live validation
TypeScript ref errorsPolymorphic componentUpdate ref type to match
component
prop value
Component won't accept custom propsPolymorphic typingProps don't extend default element; use
component
to change
Tooltip not showing on disabledDisabled blocks eventsUse
data-disabled
with
onClick
preventDefault

External Documentation

For latest Mantine documentation:

  1. Context7 (recommended):

    mcp__context7__get-library-docs
    context7CompatibleLibraryID: "/mantinedev/mantine"
    topic: "Button" or "Select" or "form" etc.
    
  2. Direct fetch:

    WebFetch → https://mantine.dev/llms.txt
    
  3. Official docs: https://mantine.dev

Component Quick Reference

See REFERENCE.md for detailed component patterns including:

  • Layout: Stack, Group, Grid, Flex, Container, AppShell
  • Inputs: TextInput, Select, Checkbox, DatePicker
  • Feedback: Modal, Notifications, Alert
  • Navigation: Tabs, NavLink, Breadcrumbs
  • Data: Table, Skeleton, Timeline

Related Agent

For comprehensive React + Mantine UI guidance that coordinates this and other Mantine skills, use the

mantine-ui-expert
agent.

Cost Optimization: Use

model="haiku"
when invoking this agent for routine component development tasks. Haiku is sufficient for template-based Mantine patterns and Styles API usage.