Animation & motionGlowing border beam
Border beam
A small traveling light traces the edge of a focused surface.

Play to follow the light
Add to your project
Component source
Standalone React component. Styles are included.
React only. No additional packages.
Render the default export inside a container with a defined height. Its optional active prop stops movement when false. This example works with React 19; Next.js projects can use it as a client component.
View source code
/**
* Adapted from https://github.com/magicuidesign/magicui/blob/85f97fd27b053339af74ba9dd4ca428ee25989fb/apps/www/registry/magicui/border-beam.tsx
* Converted Tailwind styles and Motion offset-distance to native scoped CSS; explicit border mask; muted colors; pause control and reduced-motion static frame; functional local idea form.
*
* MIT License
*
* Copyright (c) Magic UI
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
"use client"
import { useState } from "react"
export default function BorderBeam({ active = true }: { active?: boolean }) {
const [paused, setPaused] = useState(false)
const [idea, setIdea] = useState("")
const [saved, setSaved] = useState(false)
const canAnimate = active && !paused
return (
<div
data-motion-example="border-beam"
data-running={canAnimate}
style={{
width: "100%",
height: "100%",
minHeight: 240,
display: "grid",
placeItems: "center",
position: "relative",
background: "#20241f",
color: "#e7ebe4",
fontFamily: "inherit",
}}
>
<style>{`
[data-motion-example="border-beam"] textarea::placeholder { color: #a0aa97; opacity: 1; }
[data-motion-example="border-beam"] .mg-border-light { offset-distance: 35%; animation: mg-border-trace 5s linear infinite; }
[data-motion-example="border-beam"][data-running="false"] .mg-border-light { animation-play-state: paused; }
@keyframes mg-border-trace { from { offset-distance: 0%; } to { offset-distance: 100%; } }
@media (prefers-reduced-motion: reduce) { [data-motion-example="border-beam"] .mg-border-light { animation: none; offset-distance: 35%; } }
`}</style>
<form
onSubmit={(event) => {
event.preventDefault()
setSaved(true)
}}
style={{
position: "relative",
width: "min(292px, calc(100% - 48px))",
padding: 17,
marginBottom: 14,
border: "1px solid #41493d",
borderRadius: 17,
background: "#292e26",
boxShadow: "0 12px 24px #11160d30",
boxSizing: "border-box",
}}
>
<div
aria-hidden="true"
style={{
position: "absolute",
inset: -1,
borderRadius: 17,
padding: 1,
overflow: "hidden",
pointerEvents: "none",
maskImage: "linear-gradient(#fff 0 0), linear-gradient(#fff 0 0)",
maskClip: "content-box, border-box",
maskComposite: "exclude",
WebkitMaskComposite: "xor",
}}
>
<div
className="mg-border-light"
style={{
position: "absolute",
width: 90,
height: 90,
offsetPath: "rect(0 auto auto 0 round 17px)",
background: "linear-gradient(to left, #d7e8c6, #a1b990, transparent)",
}}
/>
</div>
<label style={{ display: "block", fontSize: 12, color: "#e4e8df", marginBottom: 9 }}>
What should we make?
<textarea
aria-label="Describe your idea"
value={idea}
onChange={(event) => {
setIdea(event.target.value)
setSaved(false)
}}
placeholder="Describe your idea…"
rows={2}
style={{
display: "block",
marginTop: 9,
width: "100%",
padding: "4px 0",
resize: "none",
border: 0,
borderRadius: 3,
background: "transparent",
color: "#d6ddce",
font: "inherit",
fontSize: 16,
lineHeight: 1.6,
outlineOffset: 5,
boxSizing: "border-box",
}}
/>
</label>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 }}>
<span role="status" style={{ fontSize: 10, color: "#a0aa97" }}>
{saved ? "Idea saved in this preview" : ""}
</span>
<button
type="submit"
disabled={!idea.trim()}
aria-label="Save idea in this preview"
style={{
width: 44,
height: 44,
display: "grid",
placeItems: "center",
border: "1px solid #505b47",
borderRadius: 11,
background: "#434e39",
color: "#e7efde",
cursor: idea.trim() ? "pointer" : "default",
opacity: idea.trim() ? 1 : 0.6,
outlineOffset: 3,
touchAction: "manipulation",
}}
>
<svg aria-hidden="true" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
d="M8 12V3M4 7l4-4 4 4"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
</div>
</form>
<button
type="button"
aria-pressed={paused}
onClick={() => setPaused((value) => !value)}
style={{
position: "absolute",
bottom: 2,
right: 12,
padding: "0 9px",
minHeight: 44,
border: 0,
borderRadius: 5,
background: "transparent",
color: "#b0b8a9",
font: "inherit",
fontSize: 10,
cursor: "pointer",
outlineOffset: 2,
touchAction: "manipulation",
}}
>
{paused ? "Play glow" : "Pause glow"}
</button>
</div>
)
}
Where it works well
Draw attention to a single interactive area or a temporary working state, with quiet content inside.
Reduced motion
A stationary border accent replaces the traveling light.
Things to know
Uses CSS offset-path and masking. Treat the accent as decorative, never the only status indicator.