Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Pro

Curved Loop for React

Text that flows along a curve without end, like a marquee bent into a wave. For React with Base UI, when a hero or a break between sections needs a line of text that moves.

Framework

Curved Loop runs a line of text along a curve, a gentle wave by default or any path you draw, and keeps it flowing like a marquee: the text is repeated to fill the curve and loops without a seam. It pauses while the pointer is over it, and the text can be grabbed and pulled along, either way.

Use it for a band between sections, a playful hero or a tagline that should move. For straight rows that answer the page's scroll, use Scroll Velocity; for text around a ring, Circular Text. It's SVG: the text is set along the path, so every letter keeps its own width and turns with the curve, and an SVG animation slides it along, with no animation library. A small script measures one copy of the text so the loop meets itself exactly. With reduced motion the text holds still.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { CurvedLoop } from "@/components/ui/curved-loop";
<CurvedLoop separator="✦" text="Curved Loop" />

Demos

Path

path is the curve, as SVG path data drawn in the viewBox, 0 0 1000 200 by default: the drawing scales to the width, text and all. Start and end the path past the box's sides, so the text comes in and goes out of sight, and leave room around it for the letters, which sit centred on the line. speed is the time the text takes to move by one character, 250 ms by default.

Preview width

Direction

direction is which way the text flows, left by default. The separator is its own part (data-slot="curved-loop-separator"), so it can take a colour of its own, here the primary.

Preview width

Accessibility

Screen readers read the text once, from a visually hidden copy; the drawing, its repeats and its separators are hidden from them. The text stops while the pointer is over it, for anyone reading slowly. Pulling it along is an extra for the pointer: it only moves decoration, so the keyboard needs no way to do it. With reduced motion the text holds still.

FAQ

How do I size the text?

With a font size class, text-6xl by default, in the viewBox's units: the whole drawing scales to the width, so the text keeps its size against the curve on every screen. In the default box, 1000 units wide, text-6xl is 60 units, six per cent of the width.

How does the loop never show a seam?

One copy of the text, with its separator, is measured, and the text is repeated to fill the path with a copy to spare. The animation slides it back by exactly one copy and starts again, which looks just as it did. It's measured again if the font changes, such as when a web font arrives.

How does pulling the text work?

Press on it and drag sideways: the text follows the pointer along the curve, either way, and flows on from there when you let go. On a touch screen, a sideways swipe pulls it and an upright one scrolls the page. Pass drag={false} to turn it off.

Can the path be any shape?

Any open path: an arc, a wave, a zigzag, a line that climbs. Start and end it outside the box, where the text appears and disappears. A closed shape shows the point where its ends meet; for text round a circle, use Circular Text.

Why SVG text rather than CSS motion paths?

CSS motion paths would place each letter at a fixed step, so narrow and wide letters space unevenly, and their coordinates don't scale with the box. SVG text on a path keeps each letter's real width and kerning and scales with the drawing, and an SVG animation moves it without a frame loop.

What about right-to-left pages and text?

The text always runs from the path's start to its end, so it looks the same on a right-to-left page. For right-to-left text, direction="right" lets it flow its reading way.

What happens with reduced motion?

The text holds still along the curve, from its start, and can still be read whole from the visually hidden copy.

API Reference

CurvedLoop

<span> data-slot="curved-loop"

PropType
textstring

The text, repeated along the curve.

direction?"left" | "right"

Which way the text flows along the curve. @default "left".

Type
"left" | "right"
Default
"left"
drag?boolean

Whether the text can be grabbed and pulled along the curve. @default true.

Default
true
path?string

The curve the text runs along, as SVG path data in the viewBox's units. Start and end it past the box's sides, so the text comes in and goes out of sight. By default a gentle wave.

Default
WAVE
separator?string

Drawn between one copy of the text and the next, such as ✦ or •.

speed?number

The time the text takes to move by one character, in milliseconds; lower is faster.

Default
250
viewBox?string

The box the path is drawn in; the drawing scales to the width. @default "0 0 1000 200".

Default
"0 0 1000 200"
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

All 97 text effects