Media Between Text
Media Between Text is a React and Tailwind CSS text component from Fancy Components, licensed MIT. Copy it and paste it into your project.
Fancy Components
MIT
text
What it is
Media Between Text is a React and Tailwind CSS text component from Fancy Components, licensed MIT. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
Needs
npm i motion
Licence
This component comes from Fancy Components and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.
Media Between Text
"use client"
import { ElementType, forwardRef, useImperativeHandle, useRef, useState } from "react"
import { motion, useInView, UseInViewOptions, Variants } from "motion/react"
import { cn } from "@/lib/utils"
interface MediaBetweenTextProps {
/**
* The text to display before the media
*/
firstText: string
/**
* The text to display after the media
*/
secondText: string
/**
* URL of the media (image or video) to display
*/
mediaUrl: string
/**
* Type of media to display
*/
mediaType: "image" | "video"
/**
* Optional class name for the media container
*/
mediaContainerClassName?: string
/**
* Fallback URL for video poster or image loading
*/
fallbackUrl?: string
/**
* HTML Tag to render the text elements as
* @default p
*/
as?: ElementType
/**
* Whether video should autoplay
* @default true
*/
autoPlay?: boolean
/**
* Whether video should loop
* @default true
*/
loop?: boolean
/**
* Whether video should be muted
* @default true
*/
muted?: boolean
/**
* Whether video should play inline
* @default true
*/
playsInline?: boolean
/**
* Alt text for image
*/
alt?: string
/**
* Type of animation trigger
* @default "hover"
*/
triggerType?: "hover" | "ref" | "inView"
/**
* Reference to container element for inView trigger
*/
containerRef?: React.RefObject<HTMLDivElement | null>
/**
* Options for useInView hook
*/
useInViewOptionsProp?: UseInViewOptions
/**
* Custom animation variants
*/
animationVariants?: {
initial: Variants["initial"]
animate: Variants["animate"]
}
/**
* Optional class name for the root element
*/
className?: string
/**
* Optional class name for the left text element
*/
leftTextClassName?: string
/**
* Optional class name for the right text element
*/
rightTextClassName?: string
}
export type MediaBetweenTextRef = {
animate: () => void
reset: () => void
}
export const MediaBetweenText = forwardRef<
MediaBetweenTextRef,
MediaBetweenTextProps
>(
(
{
firstText,
secondText,
mediaUrl,
mediaType,
mediaContainerClassName,
fallbackUrl,
as = "p",
autoPlay = true,
loop = true,
muted = true,
playsInline = true,
alt,
triggerType = "hover",
containerRef,
useInViewOptionsProp = {
once: true,
amount: 0.5,
root: containerRef,
},
animationVariants = {
initial: { width: 0, opacity: 1 },
animate: {
width: "auto",
opacity: 1,
transition: { duration: 0.4, type: "spring", bounce: 0 },
},
},
className,
leftTextClassName,
rightTextClassName,
},
ref
) => {
const componentRef = useRef<HTMLDivElement>(null)
const [isAnimating, setIsAnimating] = useState(false)
const isInView =
triggerType === "inView"
? useInView(componentRef || containerRef, useInViewOptionsProp)
: false
const [isHovered, setIsHovered] = useState(false)
useImperativeHandle(ref, () => ({
animate: () => setIsAnimating(true),
reset: () => setIsAnimating(false),
}))
const shouldAnimate =
triggerType === "hover"
? isHovered
: triggerType === "inView"
? isInView
: triggerType === "ref"
? isAnimating
: false
const TextComponent = motion.create(as)
return (
<div
className={cn("flex", className)}
ref={componentRef}
onMouseEnter={() => triggerType === "hover" && setIsHovered(true)}
onMouseLeave={() => triggerType === "hover" && setIsHovered(false)}
>
<TextComponent layout className={leftTextClassName}>
{firstText}
</TextComponent>
<motion.div
className={mediaContainerClassName}
variants={animationVariants}
initial="initial"
animate={shouldAnimate ? "animate" : "initial"}
>
{mediaType === "video" ? (
<video
className="w-full h-full object-cover"
autoPlay={autoPlay}
loop={loop}
muted={muted}
playsInline={playsInline}
poster={fallbackUrl}
>
<source src={mediaUrl} type="video/mp4" />
</video>
) : (
<img
src={mediaUrl}
alt={alt || `${firstText} ${secondText}`}
className="w-full h-full object-cover"
/>
)}
</motion.div>
<TextComponent layout className={rightTextClassName}>
{secondText}
</TextComponent>
</div>
)
}
)
MediaBetweenText.displayName = "MediaBetweenText"
export default MediaBetweenText