Mantine Border Animate

Undolog

@gfazioli/mantine-border-animate

Mantine component offering four border animation variants (beam, glow, gradient, pulse) with customizable colors and full animation control, perfect for creating dynamic, visually engaging UI elements.

Installation

yarn add @gfazioli/mantine-border-animate

After installation import package styles at the root of your application:

import '@gfazioli/mantine-border-animate/styles.css';

You can import styles within a layer @layer mantine-border-animate by importing @gfazioli/mantine-border-animate/styles.layer.css file.

import '@gfazioli/mantine-border-animate/styles.layer.css';

Usage

The BorderAnimate component wraps any element to add stunning animated border effects. It's perfect for creating eye-catching UI elements like cards, buttons, input fields, alerts, and more.

Key Features

  • Multiple animation variants - Choose from beam, gradient, glow, or pulse effects
  • Fully customizable - Control colors, duration, size, blur, and more
  • Works with any element - Wrap buttons, cards, inputs, or any Mantine component
  • Performance optimized - Uses CSS animations for smooth 60fps effects
  • Controllable - Toggle animation on/off, show/hide the border, set static angles

Simply wrap your content with BorderAnimate and customize it using props:

Animate Border

This is an example of BorderAnimate component

Size
Radius
Border width
Blur
Duration
Color from
Color to
import { BorderAnimate, BorderAnimateProps } from '@gfazioli/mantine-border-animate';
import { Flex, Text, Title } from '@mantine/core';

function Demo() {
  return (
    <BorderAnimate  w={500} h={400}>
      <Flex flex={1} direction="column" align="center" justify="center" h="100%" style={{ borderRadius: 'inherit', backgroundColor: 'var(--mantine-color-default)',}}>
        <Title>Animate Border</Title>
        <Text>This is an example of BorderAnimate component</Text>
      </Flex>
    </BorderAnimate>
  );
}

Controlled

You can control the border animation using the show prop.

This is a title

This is a paragraph inside the BorderAnimate component.

import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Button, Flex, Paper, Stack, Title } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function Demo() {
  const [show, { toggle }] = useDisclosure(true);

  return (
    <Flex>
      <BorderAnimate show={show} size="lg">
        <Paper withBorder shadow="md" radius="md" p="md">
          <Stack>
            <Title>This is a title</Title>
            <p>This is a paragraph inside the BorderAnimate component.</p>
            <Button onClick={toggle}>Toggle BorderAnimate</Button>
          </Stack>
        </Paper>
      </BorderAnimate>
    </Flex>
  );
}

Animated

You can also control the border animation using the animate prop. If animate is set to true, the border will animate continuously. If animate is set to false, the border will be static. Anyway, you can control the angle of the border animation using the angle prop.

This is a title

This is a paragraph inside the BorderAnimate component.

import { useState } from 'react';
import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { AngleSlider, Button, Flex, Paper, Stack, Title } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function Demo() {
  const [animate, { toggle }] = useDisclosure(true);
  const [value, setValue] = useState(0);

  return (
    <Flex>
      <BorderAnimate animate={animate} angle={value} size="lg">
        <Paper withBorder shadow="md" radius="md" p="md">
          <Stack>
            <Title>This is a title</Title>
            <p>This is a paragraph inside the BorderAnimate component.</p>
            <Button onClick={toggle}>Toggle Animation</Button>
          </Stack>
        </Paper>
      </BorderAnimate>
      <AngleSlider
        aria-label="Angle slider"
        value={value}
        onChange={setValue}
        formatLabel={(value) => `${value}°`}
      />
    </Flex>
  );
}

Variant

The BorderAnimate component supports four different animation variants, each creating a unique visual effect. Use the variant prop to switch between them.

This is a title

This is a paragraph inside the BorderAnimate component.

import { Window } from '@gfazioli/mantine-window';
import { Box, Title } from '@mantine/core';
import { h } from "../components/MdxElements/MdxElements";

function Demo() {
  return (
    <Flex>
      <BorderAnimate  size="lg">
        <Paper withBorder shadow="md" radius="md" p="md">
          <Stack>
            <Title>This is a title</Title>
            <p>This is a paragraph inside the BorderAnimate component.</p>
          </Stack>
        </Paper>
      </BorderAnimate>
    </Flex>
  );
}

Beam

The beam variant (default) creates a glowing light that travels along the border path. The beam moves continuously around the element's perimeter, following the border radius. This is ideal for highlighting interactive elements or creating a futuristic UI feel.

Key props for beam variant:

  • size - Controls the size of the beam (the glowing spot)
  • anchor - Adjusts how far inward the beam follows the border path
  • duration - Controls how fast the beam travels around the border

Gradient

The gradient variant creates a rotating conic gradient effect around the border. The gradient smoothly transitions between colorFrom and colorTo while continuously rotating. This creates an elegant, hypnotic effect suitable for loading states or premium UI elements.

Key props for gradient variant:

  • duration - Controls the rotation speed
  • blur - Adds a soft glow effect to the gradient

Glow

The glow variant produces a pulsating glow effect that fades in and out. The entire border area illuminates with a soft, diffused light that pulses rhythmically. This is perfect for attention-grabbing elements or notification indicators.

Key props for glow variant:

  • duration - Controls the pulse speed
  • blur - Adjusts the softness of the glow
  • opacity - Controls the maximum opacity of the glow

Pulse

The pulse variant creates a subtle scaling animation combined with opacity changes. The border gently expands and fades, creating a breathing effect. This variant is excellent for subtle emphasis without being too distracting.

Key props for pulse variant:

  • duration - Controls the pulse rhythm
  • blur - Softens the border effect

Mask and Background Effects

The withMask and zIndex props allow you to create advanced visual effects, including background glows and layered animations.

withMask

By default, withMask is true, which clips the animated effect to the border area only. Set withMask={false} to let the glow extend beyond the border, creating a softer, more diffused effect.

withMask=true

Clipped to border

withMask=false

Glow extends outward

import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Box, Flex, Text, Stack } from '@mantine/core';

function Content({ children }: { children: React.ReactNode }) {
  return (
    <Box
      w="100%"
      h="100%"
      p="md"
      style={{
        backgroundColor: 'var(--mantine-color-default)',
        borderRadius: 'var(--mantine-radius-md)',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
      }}
    >
      {children}
    </Box>
  );
}

function Demo() {
  return (
    <Flex gap="xl" align="center">
      {/* Default: withMask={true} - border is clipped to the edge */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={200} h={150} withMask size={200} blur={4}>
          <Content>
            <Text size="sm">withMask=true</Text>
          </Content>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Clipped to border</Text>
      </Stack>

      {/* withMask={false} - glow extends beyond the border */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={200} h={150} withMask={false} size={200} blur={4}>
          <Content>
            <Text size="sm">withMask=false</Text>
          </Content>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Glow extends outward</Text>
      </Stack>
    </Flex>
  );
}

zIndex

The zIndex prop controls the stacking order of the border effect. By default, the border appears in front of the content (zIndex={1}). Set zIndex={-1} combined with withMask={false} to create beautiful background glow effects that appear behind your content.

zIndex=1

Border in front

zIndex=-1

Background glow

Layered

Front + background

import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Box, Flex, Text, Stack } from '@mantine/core';

function Content({ children }: { children: React.ReactNode }) {
  return (
    <Box
      w="100%"
      h="100%"
      p="md"
      style={{
        backgroundColor: 'var(--mantine-color-default)',
        borderRadius: 'var(--mantine-radius-md)',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
      }}
    >
      {children}
    </Box>
  );
}

function Demo() {
  return (
    <Flex gap="xl" align="center">
      {/* Default: zIndex={1} - border is in front */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={200} h={150} zIndex={1} size={200} blur={4}>
          <Content>
            <Text size="sm">zIndex=1</Text>
          </Content>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Border in front</Text>
      </Stack>

      {/* zIndex={-1} with withMask={false} - creates background glow effect */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={200} h={150} zIndex={-1} withMask={false} size={300} blur={14} opacity={0.5}>
          <Content>
            <Text size="sm">zIndex=-1</Text>
          </Content>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Background glow</Text>
      </Stack>

      {/* Combined: layered effect with multiple borders */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={200} h={150} size={150}>
          <BorderAnimate w={200} h={150} zIndex={-1} withMask={false} size={300} blur={20} opacity={0.3} colorFrom="#ff6b6b" colorTo="#2b00ff" duration={8}>
            <Content>
              <Text size="sm">Layered</Text>
            </Content>
          </BorderAnimate>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Front + background</Text>
      </Stack>
    </Flex>
  );
}

anchor

The anchor prop controls how far inward the beam follows the border path. By default, anchor={0} makes the beam follow the outer edge. Positive values move the beam path inward, which is especially useful when combined with withMask={false} to create inner glow effects that illuminate the content area.

anchor=0

Outer edge

anchor=50

Moved inward

anchor=40

Inner glow effect

import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Box, Flex, Text, Stack } from '@mantine/core';

function Content({ children }: { children: React.ReactNode }) {
  return (
    <Box
      w="100%"
      h="100%"
      p="md"
      style={{
        backgroundColor: 'var(--mantine-color-default)',
        borderRadius: 'var(--mantine-radius-md)',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
      }}
    >
      {children}
    </Box>
  );
}

function Demo() {
  return (
    <Flex gap="xl" align="center">
      {/* anchor=0 (default) - beam follows outer edge */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={200} h={150} size={200} blur={4} anchor={0}>
          <Content>
            <Text size="sm">anchor=0</Text>
          </Content>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Outer edge</Text>
      </Stack>

      {/* anchor=50 - beam moves inward */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={200} h={150} size={200} blur={4} anchor={50}>
          <Content>
            <Text size="sm">anchor=50</Text>
          </Content>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Moved inward</Text>
      </Stack>

      {/* anchor with withMask=false - creates inner glow effect */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={200} h={150} withMask={false} size={200} blur={4} anchor={40}>
          <Content>
            <Text size="sm">anchor=40</Text>
          </Content>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Inner glow effect</Text>
      </Stack>
    </Flex>
  );
}

Combine anchor with zIndex={-1} and withMask={false} to create beautiful background glow effects. Higher anchor values move the glow closer to the center, creating different illumination patterns behind your content.

anchor=0

Outer background

anchor=30

Closer background

anchor=60

Centered glow

import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Box, Flex, Text, Stack } from '@mantine/core';

function Content({ children }: { children: React.ReactNode }) {
  return (
    <Box
      w="100%"
      h="100%"
      p="md"
      style={{
        backgroundColor: 'var(--mantine-color-default)',
        borderRadius: 'var(--mantine-radius-md)',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
      }}
    >
      {children}
    </Box>
  );
}

function Demo() {
  return (
    <Flex gap="xl" align="center">
      {/* Background glow with anchor=0 */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={200} h={150} size={250} blur={12} anchor={0} zIndex={-1} withMask={false} opacity={0.6}>
          <Content>
            <Text size="sm">anchor=0</Text>
          </Content>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Outer background</Text>
      </Stack>

      {/* Background glow with anchor=30 */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={200} h={150} size={250} blur={12} anchor={30} zIndex={-1} withMask={false} opacity={0.6}>
          <Content>
            <Text size="sm">anchor=30</Text>
          </Content>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Closer background</Text>
      </Stack>

      {/* Background glow with anchor=60 - centered behind content */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={200} h={150} size={250} blur={16} anchor={60} zIndex={-1} withMask={false} opacity={0.5}>
          <Content>
            <Text size="sm">anchor=60</Text>
          </Content>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Centered glow</Text>
      </Stack>
    </Flex>
  );
}

Use Cases

BorderAnimate can be used with virtually any Mantine component. Here are some common use cases to inspire your implementations.

Buttons

Add animated borders to buttons to make them stand out. Perfect for call-to-action buttons, submit buttons, or any interactive element that needs extra attention.

import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Button, Flex } from '@mantine/core';

function Demo() {
  return (
    <Flex gap="xl" align="center">
      <BorderAnimate radius={4} size={80}>
        <Button>Click me</Button>
      </BorderAnimate>

      <BorderAnimate radius={256} size={80}>
        <Button radius={256} variant="default">
          Rounded
        </Button>
      </BorderAnimate>

      <BorderAnimate radius="md" size={100} variant="gradient">
        <Button variant="light" color="violet">
          Gradient
        </Button>
      </BorderAnimate>
    </Flex>
  );
}

Input Fields

Highlight input fields with animated borders to guide users through forms or indicate active/focused states.

import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Input } from '@mantine/core';

function Demo() {
  return (
    <BorderAnimate size="xs" radius="sm">
      <Input placeholder="Your email" />
    </BorderAnimate>
  );
}

Alerts and Notifications

Make alerts more noticeable with animated borders. Use different variants and colors to convey urgency levels.

import { IconInfoCircle, IconAlertTriangle } from '@tabler/icons-react';
import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Alert, Stack } from '@mantine/core';

function Demo() {
  return (
    <Stack gap="md" w={400}>
      <BorderAnimate size={200} radius="sm" duration={10}>
        <Alert
          variant="light"
          color="blue"
          title="Information"
          icon={<IconInfoCircle />}
        >
          This is an informational alert with an animated border effect.
        </Alert>
      </BorderAnimate>

      <BorderAnimate
        size={200}
        radius="sm"
        duration={8}
        colorFrom="red"
        colorTo="orange"
        variant="pulse"
      >
        <Alert
          variant="light"
          color="red"
          title="Warning"
          icon={<IconAlertTriangle />}
        >
          This is a warning alert with a pulsing border effect.
        </Alert>
      </BorderAnimate>
    </Stack>
  );
}

Cards

Create premium-looking cards with animated borders. Great for featured content, pricing cards, or special offers.

Norway

Premium Package

On Sale

Get access to all premium features with our special animated border effect that highlights this exclusive offer.

import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Card, Image, Text, Badge, Button, Group } from '@mantine/core';

function Demo() {
  return (
    <BorderAnimate variant="glow" blur="md" radius="md" duration={3}>
      <Card shadow="sm" padding="lg" radius="md" withBorder w={340}>
        <Card.Section>
          <Image
            src="https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/images/bg-8.png"
            height={160}
            alt="Norway"
          />
        </Card.Section>

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

        <Text size="sm" c="dimmed">
          Get access to all premium features with our special animated
          border effect that highlights this exclusive offer.
        </Text>

        <Button color="blue" fullWidth mt="md" radius="md">
          Get Started
        </Button>
      </Card>
    </BorderAnimate>
  );
}

Chips and Badges

Even small elements like chips can benefit from animated borders to indicate selection or special status.

import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Chip, Flex } from '@mantine/core';

function Demo() {
  return (
    <Flex gap="md" align="center">
      <BorderAnimate size="xs" radius="xl">
        <Chip defaultChecked>Selected</Chip>
      </BorderAnimate>

      <BorderAnimate size="xs" radius="xl" colorFrom="green" colorTo="teal">
        <Chip defaultChecked color="green">Active</Chip>
      </BorderAnimate>

      <BorderAnimate size="xs" radius="xl" variant="gradient" duration={2}>
        <Chip defaultChecked color="violet">Premium</Chip>
      </BorderAnimate>
    </Flex>
  );
}

Accordion

Wrap accordion components with animated borders to create visually engaging expandable sections. The border effect adds a modern touch to FAQ sections or collapsible content areas.

Crisp and refreshing fruit. Apples are known for their versatility and nutritional benefits.
import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Accordion } from '@mantine/core';

const data = [
  {
    emoji: '🍎',
    value: 'Apples',
    description:
      'Crisp and refreshing fruit. Apples are known for their versatility and nutritional benefits.',
  },
  {
    emoji: '🍌',
    value: 'Bananas',
    description:
      'Naturally sweet and potassium-rich fruit. Bananas are a popular choice for energy-boosting.',
  },
  {
    emoji: '🥦',
    value: 'Broccoli',
    description:
      'Nutrient-packed green vegetable. Broccoli is packed with vitamins, minerals, and fiber.',
  },
];

function Demo() {
  const items = data.map((item) => (
    <Accordion.Item key={item.value} value={item.value}>
      <Accordion.Control icon={item.emoji}>{item.value}</Accordion.Control>
      <Accordion.Panel>{item.description}</Accordion.Panel>
    </Accordion.Item>
  ));

  return (
    <BorderAnimate size="lg" radius="md">
      <Accordion variant="contained" defaultValue="Apples">
        {items}
      </Accordion>
    </BorderAnimate>
  );
}

Multiple Borders

Nest multiple BorderAnimate components to create complex, layered effects. Combine different variants, speeds, directions, and colors to achieve unique visual compositions. This technique is perfect for hero sections or premium UI elements that need extra visual impact.

Multiple Animated Borders

import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Box, Flex, Text } from '@mantine/core';

function Content({ children }: { children: React.ReactNode }) {
  return (
    <Box
      w="100%"
      h="100%"
      p="md"
      style={{
        backgroundColor: 'var(--mantine-color-default)',
        borderRadius: 'var(--mantine-radius-md)',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
      }}
    >
      {children}
    </Box>
  );
}

function Demo() {
  return (
    <BorderAnimate w={400} h={250}>
      <BorderAnimate w={400} h={250} duration={55} reverse borderWidth={1} size={400} colorFrom="#ff6b6b" colorTo="#2b00ff">
        <BorderAnimate w={400} h={250} duration={23} withMask={false} size={300} opacity={0.2} blur={14} anchor={50} zIndex={-1}>
          <BorderAnimate w={400} h={250} variant="glow" blur={4}>
            <Content>
              <Text fw={500}>Multiple Animated Borders</Text>
            </Content>
          </BorderAnimate>
        </BorderAnimate>
      </BorderAnimate>
    </BorderAnimate>
  );
}

Circular Elements

BorderAnimate works perfectly with circular shapes. Set radius="100%" to create animated borders around avatars, profile pictures, or any circular UI element. The beam smoothly follows the circular path.

Circle

Simple circle

Avatar

Avatar

Gradient

Gradient variant

import { BorderAnimate } from '@gfazioli/mantine-border-animate';
import { Avatar, Box, Flex, Stack, Text } from '@mantine/core';

function CircleContent({ children }: { children: React.ReactNode }) {
  return (
    <Box
      w="100%"
      h="100%"
      style={{
        backgroundColor: 'var(--mantine-color-default)',
        borderRadius: '100%',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
      }}
    >
      {children}
    </Box>
  );
}

function Demo() {
  return (
    <Flex gap="xl" align="center">
      {/* Simple circle */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={100} h={100} radius="100%">
          <CircleContent>
            <Text size="xs">Circle</Text>
          </CircleContent>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Simple circle</Text>
      </Stack>

      {/* Avatar with glow */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={80} h={80} variant="glow" radius="100%" size={60} colorFrom="green" colorTo="cyan">
          <Avatar
            src="https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-1.png"
            alt="Avatar"
            radius="100%"
            size={80}
          />
        </BorderAnimate>
        <Text size="xs" c="dimmed">Avatar</Text>
      </Stack>

      {/* Larger circle with gradient variant */}
      <Stack align="center" gap="xs">
        <BorderAnimate w={120} h={120} radius="100%" variant="gradient" duration={3}>
          <CircleContent>
            <Text size="xs">Gradient</Text>
          </CircleContent>
        </BorderAnimate>
        <Text size="xs" c="dimmed">Gradient variant</Text>
      </Stack>
    </Flex>
  );
}