# Fluid Gradient Text

Render text with a fluid gradient that shifts with pointer movement.

```tsx
import { FluidGradientText } from "@/components/fluid-gradient-text"

export default function FluidGradientTextDemo() {
  return (
    <div className="relative w-full text-foreground">
      <div className="pointer-events-none absolute inset-x-0 top-0 text-center text-xs text-muted-foreground select-none">
        <span className="hidden pointer-fine:inline-block">
          Move your cursor within the text below
        </span>
        <span className="hidden pointer-coarse:inline-block">
          Press anywhere within the text below
        </span>
      </div>

      <FluidGradientText text="shadcn" />
    </div>
  )
}

```

## Features

* Gradient shifts with horizontal pointer movement for an interactive text effect.
* Smooth transitions keep the effect natural while moving across the text.
* `svgViewBoxWidth` and `svgViewBoxHeight` let you tune text scale for different layouts.

## Installation

<CodeTabs>
  <TabsListInstallType />

  <TabsContent value="cli">
    ```bash
    npx shadcn@latest add @ncdai/fluid-gradient-text
    ```
  </TabsContent>

  <TabsContent value="manual">
    <Steps>
      <Step>Install the following dependencies</Step>

      ```bash
      npm install motion
      ```

      <Step>Copy and paste the following code into your project</Step>

      ```tsx title="components/fluid-gradient-text.tsx" 
      "use client"

      import { motion, useMotionValue, useSpring } from "motion/react"

      export type FluidGradientTextProps = {
        /** Text content rendered inside the SVG. */
        text: string
        /**
         * SVG viewBox width used to scale the gradient and text layout.
         * @default 1200
         * */
        svgViewBoxWidth?: number
        /**
         * SVG viewBox height used as the base text size.
         * @default 300
         * */
        svgViewBoxHeight?: number
      }

      export function FluidGradientText({
        text,
        svgViewBoxWidth = 1200,
        svgViewBoxHeight = 300,
      }: FluidGradientTextProps) {
        const gradientX1Raw = useMotionValue(svgViewBoxWidth / 2)
        const gradientX1 = useSpring(gradientX1Raw, {
          stiffness: 200,
          damping: 30,
          mass: 0.5,
        })

        const handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
          const container = event.currentTarget
          const containerRect = container.getBoundingClientRect()
          const mouseX = event.clientX - containerRect.left
          const containerWidth = containerRect.width

          const normalizedX = (mouseX / containerWidth) * svgViewBoxWidth
          const clampedX = Math.max(0, Math.min(svgViewBoxWidth, normalizedX))

          gradientX1Raw.set(clampedX)
        }

        const handleMouseLeave = () => {
          gradientX1Raw.set(svgViewBoxWidth / 2)
        }

        return (
          <div
            className="relative size-full overflow-hidden after:absolute after:bottom-0 after:h-px after:w-full after:bg-current/15"
            onMouseMove={handleMouseMove}
            onMouseLeave={handleMouseLeave}
          >
            <svg
              className="size-full translate-y-[37.5%] select-none"
              viewBox={`0 0 ${svgViewBoxWidth} ${svgViewBoxHeight}`}
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
            >
              <text
                x="50%"
                y="50%"
                textAnchor="middle"
                dominantBaseline="central"
                stroke="currentColor"
                strokeOpacity="0.1"
                strokeWidth="2"
                fill="url(#fluid_gradient_text_linear)"
                style={{
                  fontFamily: "Helvetica",
                  fontSize: svgViewBoxHeight,
                  fontWeight: "bold",
                }}
              >
                {text}
              </text>
              <defs>
                <motion.linearGradient
                  id="fluid_gradient_text_linear"
                  x1={gradientX1}
                  y1="0"
                  x2={svgViewBoxWidth / 2}
                  y2={svgViewBoxHeight}
                  gradientUnits="userSpaceOnUse"
                >
                  <stop offset="0.625" stopColor="currentColor" stopOpacity="0" />
                  <stop offset="1" stopColor="currentColor" />
                </motion.linearGradient>
              </defs>
            </svg>
          </div>
        )
      }

      ```

      <Step>Update the import paths to match your project setup</Step>
    </Steps>
  </TabsContent>
</CodeTabs>

## Usage

```tsx
import { FluidGradientText } from "@/components/fluid-gradient-text"
```

```tsx
<FluidGradientText text="shadcn" />
```

## API Reference

### FluidGradientText

<TypeTable
  id="type-table-fluid-gradient-text.tsx-FluidGradientTextProps"
  type={{
  "id": "fluid-gradient-text.tsx-FluidGradientTextProps",
  "name": "FluidGradientTextProps",
  "description": "",
  "entries": [
    {
      "name": "text",
      "description": "Text content rendered inside the SVG.",
      "tags": [],
      "type": "string",
      "simplifiedType": "string",
      "required": true,
      "deprecated": false
    },
    {
      "name": "svgViewBoxWidth",
      "description": "SVG viewBox width used to scale the gradient and text layout.",
      "tags": [
        {
          "name": "default",
          "text": "1200"
        }
      ],
      "type": "number | undefined",
      "simplifiedType": "number",
      "required": false,
      "deprecated": false
    },
    {
      "name": "svgViewBoxHeight",
      "description": "SVG viewBox height used as the base text size.",
      "tags": [
        {
          "name": "default",
          "text": "300"
        }
      ],
      "type": "number | undefined",
      "simplifiedType": "number",
      "required": false,
      "deprecated": false
    }
  ]
}}
/>

## Credits

* [Vercel](https://vercel.com/design)


Last updated on May 3, 2026