NamelessUI

Writing Text

A text component that smoothly reveals content word by word.

Loading...
import { WritingText } from "@/components/core/writing-text";

export default function WritingTextExample() {
  return (
    <WritingText
      className="text-4xl"
      text="Writing Text With Effect"
      spacing={9}
    />
  );
}

Installation

Install the following dependencies:
npm install motion
Copy and paste the following code into your project:
"use client";

import * as React from "react";
import {
  motion,
  useInView,
  type Transition,
  type UseInViewOptions,
} from "motion/react";

type WritingTextProps = Omit<React.ComponentProps<"span">, "children"> & {
  transition?: Transition;
  inView?: boolean;
  inViewMargin?: UseInViewOptions["margin"];
  inViewOnce?: boolean;
  spacing?: number | string;
  text: string;
};

function WritingText({
  ref,
  inView = false,
  inViewMargin = "0px",
  inViewOnce = true,
  spacing = 5,
  text,
  transition = { type: "spring", bounce: 0, duration: 2, delay: 0.5 },
  ...props
}: WritingTextProps) {
  const localRef = React.useRef<HTMLSpanElement>(null);
  React.useImperativeHandle(ref, () => localRef.current as HTMLSpanElement);

  const inViewResult = useInView(localRef, {
    once: inViewOnce,
    margin: inViewMargin,
  });
  const isInView = !inView || inViewResult;

  const words = React.useMemo(() => text.split(" "), [text]);

  return (
    <span ref={localRef} data-slot="writing-text" {...props}>
      {words.map((word, index) => (
        <motion.span
          key={index}
          className="inline-block will-change-transform will-change-opacity"
          style={{ marginRight: spacing }}
          initial={{ opacity: 0, y: 10 }}
          animate={isInView ? { opacity: 1, y: 0 } : undefined}
          transition={{
            ...transition,
            delay: index * (transition?.delay ?? 0),
          }}
        >
          {word}{" "}
        </motion.span>
      ))}
    </span>
  );
}

export { WritingText, type WritingTextProps };
Update the import paths to match your project setup.

Usage

import { WritingText } from "@/components/core/writing-text";

export default function WritingTextExample() {
  return (
    <WritingText
      className="text-4xl"
      text="Writing Text With Effect"
      spacing={9}
    />
  );
}

Props

PropTypeDefault
className?
string
-
duration?
number
2
delay?
number
0.5
inView?
boolean
false
inViewMargin?
string
0px
inViewOnce?
boolean
true
spacing?
number | string
5
text
string
-
transition?
Transition
{ type: 'spring', bounce: 0, duration: 2, delay: 0.5 }