Web InnoventixFreeCode

Image In Text Effect

Original · free

A headline whose letters are filled with the photo.

byWeb InnoventixReact + Tailwind
imageintexteffect
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/imgfx-image-in-text.json
imgfx-image-in-text.tsx
"use client";

import { useRef } from "react";
import { motion, useInView, useReducedMotion } from "motion/react";

export default function ImgfxImageInText() {
  const rootRef = useRef<HTMLDivElement>(null);
  const inView = useInView(rootRef, { once: true, amount: 0.4 });
  const reduce = useReducedMotion();

  const image = "/img/gallery/g14.webp";

  return (
    <section className="relative w-full overflow-hidden bg-neutral-50 px-6 py-24 text-neutral-900 sm:px-10 sm:py-32 dark:bg-neutral-950 dark:text-neutral-100">
      <style>{`
        @keyframes iit-pan {
          0%   { background-position: 0% 50%; }
          50%  { background-position: 100% 50%; }
          100% { background-position: 0% 50%; }
        }
        @keyframes iit-rise {
          from { opacity: 0; transform: translateY(14px); }
          to   { opacity: 1; transform: translateY(0); }
        }
        .iit-fill {
          background-image: var(--iit-img);
          background-size: 140% auto;
          background-position: 0% 50%;
          background-repeat: no-repeat;
          -webkit-background-clip: text;
          background-clip: text;
          color: transparent;
          -webkit-text-fill-color: transparent;
          animation: iit-pan 22s ease-in-out infinite;
        }
        @media (prefers-reduced-motion: reduce) {
          .iit-fill { animation: none; background-size: cover; background-position: center; }
        }
      `}</style>

      {/* soft ambient glow */}
      <div
        aria-hidden
        className="pointer-events-none absolute left-1/2 top-1/3 h-[38rem] w-[38rem] -translate-x-1/2 rounded-full bg-indigo-300/25 blur-3xl dark:bg-indigo-600/15"
      />

      <div ref={rootRef} className="relative mx-auto flex max-w-5xl flex-col items-center text-center">
        <motion.p
          initial={reduce ? false : { opacity: 0, y: 10 }}
          animate={inView ? { opacity: 1, y: 0 } : undefined}
          transition={{ duration: 0.5, ease: "easeOut" }}
          className="mb-6 inline-flex items-center gap-2 rounded-full border border-neutral-300/70 bg-white/60 px-4 py-1.5 text-xs font-medium uppercase tracking-[0.2em] text-neutral-500 backdrop-blur dark:border-neutral-700/70 dark:bg-neutral-900/60 dark:text-neutral-400"
        >
          <span className="h-1.5 w-1.5 rounded-full bg-indigo-500" />
          Image-in-Text
        </motion.p>

        <motion.h2
          initial={reduce ? false : { opacity: 0, scale: 0.97 }}
          animate={inView ? { opacity: 1, scale: 1 } : undefined}
          transition={{ duration: 0.7, ease: [0.16, 1, 0.3, 1] }}
          className="iit-fill select-none text-[19vw] font-black leading-[0.85] tracking-tighter sm:text-[16vw] lg:text-[13rem]"
          style={{ ["--iit-img" as string]: `url(${image})` }}
        >
          WANDER
        </motion.h2>

        {/* accessible + screen-reader-safe rendering of the same word */}
        <span className="sr-only">Wander</span>

        <motion.p
          initial={reduce ? false : { opacity: 0, y: 12 }}
          animate={inView ? { opacity: 1, y: 0 } : undefined}
          transition={{ duration: 0.6, ease: "easeOut", delay: 0.15 }}
          className="mt-8 max-w-md text-sm text-neutral-500 sm:text-base dark:text-neutral-400"
        >
          The photo lives inside the letters &mdash; type becomes a window, not a label.
        </motion.p>

        {/* the source photo, shown small so the effect's origin is legible */}
        <motion.figure
          initial={reduce ? false : { opacity: 0, y: 16 }}
          animate={inView ? { opacity: 1, y: 0 } : undefined}
          transition={{ duration: 0.6, ease: "easeOut", delay: 0.3 }}
          className="mt-12 flex items-center gap-3 rounded-xl border border-neutral-200 bg-white/70 p-2 pr-4 shadow-sm backdrop-blur dark:border-neutral-800 dark:bg-neutral-900/70"
        >
          {/* eslint-disable-next-line @next/next/no-img-element */}
          <img
            src={image}
            alt="Mountain valley at dawn, the source photograph filling the headline"
            loading="lazy"
            draggable={false}
            className="h-12 w-16 rounded-lg object-cover"
          />
          <figcaption className="text-left text-xs leading-tight text-neutral-500 dark:text-neutral-400">
            Source photo
            <br />
            <span className="text-neutral-400 dark:text-neutral-500">clipped to the type</span>
          </figcaption>
        </motion.figure>
      </div>
    </section>
  );
}

Dependencies

motion

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →