How to Create a Moving Border Effect in React + Tailwind
Learn how to build a stunning moving border effect in React and Tailwind CSS that adds dynamic, animated outlines to any component for free.
What Is a Moving Border Effect?
A moving border effect is an animated outline that travels around the perimeter of an element — giving cards, buttons, and containers a sense of energy and motion without any JavaScript-heavy libraries. The effect works by rotating a gradient or a conic-gradient pseudo-element behind the element's real content, creating the illusion that a glowing beam of light is racing around the edge.
This technique has exploded in popularity alongside the rise of dark-mode-first design systems and styles like glassmorphism and neobrutalism. When applied to call-to-action buttons or highlighted cards, a moving border immediately draws the user's eye and signals interactivity — all with a few lines of CSS and Tailwind utility classes.
The best part: you do not need to pay for a premium component library. Empire UI ships a ready-made <MovingBorder> component at zero cost as part of its free component collection. But understanding how to build one from scratch gives you full creative control over speed, colour, width, and border-radius.
How the CSS Conic-Gradient Trick Works
At the heart of every moving border is a conic gradient — a gradient that sweeps around a center point like the hands of a clock. By placing this gradient inside a ::before or ::after pseudo-element that is slightly larger than the real element, then rotating it with a CSS @keyframes animation, the visible slice of the gradient peeks out beyond the clipped edges and appears to chase itself around the border.
The key CSS properties are conic-gradient, animation: spin linear infinite, and careful use of overflow: hidden on the wrapper plus an inner z-index stacking context for the content. Tailwind's animate-spin utility provides a good starting point, but custom durations (e.g. [animation-duration:3s]) give you fine-grained control over perceived speed.
It is worth noting that conic-gradient has excellent browser support across all modern browsers (Chrome 69+, Firefox 83+, Safari 12.1+). You can safely use it in production without a polyfill in 2026.
Building a Moving Border Component in React
Let's build a reusable MovingBorder wrapper component using React and Tailwind. The approach uses a div as the outer mask layer and an absolutely-positioned spinning gradient element behind the children.
``tsx
// components/MovingBorder.tsx
import { ReactNode, CSSProperties } from 'react';
interface MovingBorderProps {
children: ReactNode;
duration?: number; // seconds
borderWidth?: number; // px
gradient?: string;
borderRadius?: string;
className?: string;
}
export function MovingBorder({
children,
duration = 3,
borderWidth = 2,
gradient = 'conic-gradient(from 0deg, transparent 0%, #6366f1 30%, #a855f7 60%, transparent 100%)',
borderRadius = '0.75rem',
className = '',
}: MovingBorderProps) {
const style: CSSProperties = {
'--duration': ${duration}s,
'--border-width': ${borderWidth}px,
'--gradient': gradient,
'--radius': borderRadius,
} as CSSProperties;
return (
<div
style={style}
className={relative p-[var(--border-width)] rounded-[var(--radius)] overflow-hidden ${className}}
>
{/* spinning gradient layer */}
<div
className="absolute inset-0 animate-spin"
style={{
background: gradient,
animationDuration: ${duration}s,
borderRadius,
}}
/>
{/* content layer */}
<div
className="relative z-10 bg-gray-950 rounded-[calc(var(--radius)-var(--border-width))] h-full w-full"
>
{children}
</div>
</div>
);
}
`
Drop this file into your components/ folder and import it anywhere. The wrapper accepts duration, borderWidth, gradient, and borderRadius` props so every instance can look distinct without duplicating CSS.
To use it on a card you simply wrap your content: <MovingBorder duration={4} borderWidth={3}><YourCard /></MovingBorder>. For a glowing button variant, pass a tighter radius and a hotter gradient such as conic-gradient(from 0deg, transparent, #f97316, transparent) to get an orange arc. Explore the tools section on Empire UI for a live gradient generator that makes picking these values instant.
Performance tip: applying will-change: transform to the spinning layer via a Tailwind arbitrary value ([will-change:transform]) prevents the browser from re-painting surrounding elements on every animation frame, which is critical if you place several moving-border cards on the same page.
Adding the Animation to tailwind.config.js
Tailwind's built-in animate-spin class defaults to a 1-second rotation, which is far too fast for a border effect. You need to extend the theme with a slower spin-slow keyframe. Open your tailwind.config.ts and add the following:
``ts
// tailwind.config.ts
import type { Config } from 'tailwindcss';
const config: Config = {
content: ['./src/**/*.{ts,tsx}'],
theme: {
extend: {
animation: {
'spin-slow': 'spin 4s linear infinite',
'spin-medium': 'spin 2.5s linear infinite',
},
},
},
plugins: [],
};
export default config;
`
Now replace animate-spin in the component above with animate-spin-slow for a calmer, more elegant sweep. You can expose this as a prop (speed: 'slow' | 'medium' | 'fast'`) so designers can choose per-use-case without touching config files.
If you are using Tailwind v4's CSS-first configuration, add the equivalent @keyframes block and @utility declaration in your main CSS file instead. The animation name stays the same; only the authoring surface changes. Check the Empire UI MCP server docs — the AI-native generation pipeline already emits the correct config block when you prompt it to generate a moving-border card.
Styling Variants: Glassmorphism, Neobrutalism, and More
A moving border pairs naturally with different visual styles available in Empire UI's 40-style system. For a glassmorphism variant, set the inner content layer's background to bg-white/10 backdrop-blur-md instead of a solid dark colour, and use a pastel gradient such as conic-gradient(from 0deg, transparent, rgba(255,255,255,0.6), transparent). The translucent card with a soft-white arc feels luxurious and works well on hero sections. See the glassmorphism style hub for ready-made token values.
For a neobrutalism take, swap the gradient for a single solid-colour arc conic-gradient(from 0deg, transparent 0%, #000 5%, transparent 10%) and increase borderWidth to 4–6 px. The thick, hard-edged moving border on a flat background creates an almost mechanical, kinetic feel that aligns perfectly with bold typography. Browse /templates to find industry-specific neobrutalist layouts that are pre-wired with these animations.
You can further extend the effect by combining it with a custom cursor that reacts to hover — when the user mouses over the component you swap animate-spin-slow to animate-spin-medium via a React state toggle, making the border visibly accelerate as though excited by the cursor's proximity. This micro-interaction detail is what separates polished product UIs from cookie-cutter ones.
Empire UI's blog contains deep-dives into every major visual style. Pairing moving borders with aurora backgrounds, spotlight effects, or particle fields is especially effective for SaaS landing pages — all built with zero licensing cost.
Accessibility and Production Considerations
Animated UI elements must respect the prefers-reduced-motion media query. Wrap the spinning layer in a conditional so that users who have enabled reduced-motion in their OS see a static gradient border instead of a rotating one. In Tailwind this is as simple as adding the motion-safe: variant prefix: motion-safe:animate-spin-slow. For the non-animated fallback, apply a bg-gradient-to-r class directly on the wrapper so a decorative border is still visible — just frozen in place.
On low-end devices, running multiple simultaneous CSS animations can cause jank. Benchmark your page with Chrome DevTools' Performance panel and look for long paint times. If multiple moving borders exist in the viewport simultaneously, consider pausing off-screen instances using an IntersectionObserver that adds and removes the animation class. Empire UI's <MovingBorder> component in the collection already ships with this optimisation built in — another reason to use the library rather than rolling everything by hand.
Finally, ensure sufficient colour contrast between the moving gradient arc and the surrounding background. A faint light-grey arc on a white background fails WCAG AA for decorative elements that users may interpret as a focus indicator. If the component is keyboard-focusable, supplement the moving border with a genuine :focus-visible outline so the animation does not replace the accessibility affordance — it only augments it.
FAQ
A moving border effect is an animated outline created by rotating a conic gradient behind an element so that a glowing arc appears to travel around its perimeter. It is achieved entirely with CSS animations and requires no JavaScript framework-specific APIs.
Yes — conic-gradient is supported in Chrome 69+, Firefox 83+, Safari 12.1+, and all modern mobile browsers as of 2026. No polyfill is needed for production use, and the animation degrades gracefully to a static border in very old browsers.
Override the animation duration on the spinning element — in Tailwind you can extend the config with a custom spin-slow animation or use an arbitrary value like [animation-duration:4s]. Longer durations produce a slow, elegant sweep while shorter durations create a rapid, energetic feel.
Absolutely. Wrap your <button> with the <MovingBorder> component, set a small borderWidth (1–2 px) and a vibrant gradient, and match the borderRadius to the button's own radius. The result is a glowing call-to-action that draws the eye without sacrificing click-area ergonomics.
Empire UI's pre-built component includes IntersectionObserver-based pause logic for off-screen elements, built-in prefers-reduced-motion support, and prop-driven style variants aligned to all 40 visual styles in the library. Building from scratch gives full creative control, but the Empire UI version saves time and ships accessibility out of the box.