Installation

Follow these simple steps to add MosaicUI components to your React project:

Steps

1. Choose a Component

Browse the components library and select the component you want to use.

2. Install Dependencies (If Required)

Navigate to the code tab of the component to see the installation instructions for any required dependencies.

Some components rely on additional packages like react-three-fiber, or framer-motion. Only install these when needed.

Terminal / Command Propmpt

npm install react-thee-fiber

3. Copy the Code

Each component provides a code tab containing the component file and a corresponding CSS module.


3.1. Copy the Component File

Copy the component code into your reusable components folder in your project (For example: /src/components/)

decrypting-text-animation.jsx

import { memo, useEffect, useMemo, useState } from "react";
import styles from "./decrypting-text-animation.module.css";

const random = (n = 1) => {
  return Math.floor(Math.random() * n);
};

const DecryptingTextAnimation = (props) => {
  const {
    text,
    speed = 50,
    charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%&*-+?",
  } = props;

  const letterVariants = ["outlined", "filled"];

  const getRandomLetterVariant = () => {
    return letterVariants[random(letterVariants.length)];
  };

  const getTextMappings = () => {
    return (
      text
        ?.split(" ")
        .filter(Boolean)
        .map(word => (
          word
            .split("")
            .map((letter) => ({
              letter,
              variant: getRandomLetterVariant(),
              index: random(charset.length),
            }))
        ))
    );
  };

  const [currentText, setCurrentText] = useState(text);
  const [textMapping, setTextMapping] = useState(getTextMappings);

  const shuffledCharset = useMemo(() => {
    return charset.split("").sort(() => (
      random(5) - random(5)
    )).join("");
  }, [charset, text]);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setTextMapping((prev) => {
        return prev.map(word => (
          word.map(entry => {
            const isDone = entry.letter === shuffledCharset[entry.index];
            return {
              ...entry,
              variant: isDone ? "" : getRandomLetterVariant(),
              index: isDone ? (
                entry.index
              ) : (
                (entry.index + 1) % shuffledCharset.length
              )
            };
          })
        ))
      });
      const isDone = textMapping.flat().every(entry => {
        return entry.letter === shuffledCharset[entry.index];
      });
      if (isDone) {
        clearInterval(intervalId);
        return;
      }
    }, speed);
    return () => {
      clearInterval(intervalId);
    };
  }, [shuffledCharset, textMapping, speed]);

  if (currentText !== text) {
    setCurrentText(text);
    setTextMapping(getTextMappings());
  }

  return (
    <span
      aria-label={currentText}
      className={styles["decrypting-text"]}
    >
      {
        textMapping.map((word, wordIndex, arr) => (
          <>
            <span 
              aria-hidden={true}
              className={styles["word"]}
            >
              {word.map((letter) => {
                return (
                  <span
                    aria-hidden={true}
                    className={[
                      styles["letter"],
                      styles[`letter-${letter.variant}`]
                    ].join(" ")}
                  >
                    {shuffledCharset[letter.index]}
                  </span>
                );
              })}
            </span>
            {wordIndex !== (arr.length - 1) && (
              <span aria-hidden={true}>
                &nbsp;
              </span>
            )}
          </>
        ))
      }
    </span>
  )
};

export default memo(DecryptingTextAnimation);

3.2. Copy the CSS Module File

Copy the associated CSS module file into the same folder:

decrypting-text-animation.module.css

.decrypting-text {
  .word {
    white-space: nowrap;
    word-break: keep-all;

    .letter {
      display: inline-block;
      font: inherit;
      transition: all 100ms ease-in-out;

      &.letter-filled {
        background: currentColor;
      }

      &.letter-outlined {
        box-shadow: inset 0 0 1px 1px currentColor;
      }
    }
  }
}

Using CSS Modules ensures:

  • Scoped styles, preventing class name collisions
  • Seamless integration with React components

4. Use the Component in Your Project

Import the component into your React app and render it. Follow the example code provided with the component, and check the Props section to see which props are supported for customization.

decrypting-text-animation-preview.jsx

import DecryptingTextAnimation from "@/components/text-effects/decrypting-text-animation/decrypting-text-animation";

/* keep text on center */
const wrapperStyles = {
  textAlign: "center",
  padding: "24px",
  textWrap: "balance",
};

const DecryptingTextAnimationPreview = () => {
  return (
    <div style={wrapperStyles}>
      <p style={{
        fontFamily: "monospace",
        fontSize: "1.5rem",
      }}>
        Beyond the encrypted noise lives {" "}
        <strong>
          <DecryptingTextAnimation
            text="Pure Awareness"
            speed={50}
          />
        </strong>
      </p>
    </div>
  );
};

export default DecryptingTextAnimationPreview;