Animated text effects with multiple styles — add to your site in seconds
Copy URL or Code
Paste to your AI coding assistant and say:
“Add this effect to my headline”
“Make it pink and slower”
“Change the cursor to a hammer”
Done. Your AI handles the rest.
Fully customizable. Ask your AI to change the text, colors, animation style, or add new effects.
/*
================================================================================
AI COMPONENT: KineticTypography
================================================================================
SETUP:
1. Create file: components/KineticTypography.tsx
2. Copy this entire code block into that file
3. Import: import KineticTypography from "@/components/KineticTypography"
================================================================================
QUICK CUSTOMIZATION (via props)
================================================================================
| Prop | Type | Default |
|---------------|-------------------------------------------|----------------------------|
| text | string | "AI Native Components" |
| style | "glitch" | "bounce" | "gradient" | "magnify" | "glitch" |
| fontSize | string (CSS) | "clamp(2rem, 8vw, 5rem)" |
| colors.primary| string (hex) | "#ffffff" |
| colors.accent | string (hex) - hover color | "#ff006e" |
| showControls | boolean - show style switcher buttons | true |
| staggerDelay | number (seconds) - delay between chars | 0.05 |
================================================================================
ANIMATION SPEEDS (edit in CSS section below)
================================================================================
GLITCH:
- Base animation: 0.8s (search: "kt-glitch 0.8s")
- Hover intense: 0.1s (search: "kt-glitch-intense 0.1s")
BOUNCE:
- Hover animation: 0.33s (search: "kt-bounce 0.33s")
- Jump height: -20px (search: "translateY(-20px)")
GRADIENT:
- Flow speed: 4s (search: "kt-gradientFlow 4s")
- Colors: #ff006e, #8338ec, #3a86ff (search: "linear-gradient")
MAGNIFY:
- Transition: 0.15s (search: "0.15s ease")
- Scale: 1.4 (search: "scale(1.4)")
- Font weight on hover: 900
================================================================================
CURSOR ICONS (SVG data URIs - replace entire url() to change)
================================================================================
GLITCH: Lightning bolt (gold #FFD700)
BOUNCE: Fire flame (orange #F7931E, yellow #FCEE21)
GRADIENT: Music note (purple #8338ec)
MAGNIFY: Magnifying glass (black #000000)
To change cursor color, find the hex code in the SVG (e.g., %23FFD700) and replace.
%23 = # in URL encoding
================================================================================
COMMON MODIFICATIONS
================================================================================
1. CHANGE TEXT:
<KineticTypography text="Your Text Here" />
2. SINGLE STYLE (no buttons):
<KineticTypography text="Hello" style="glitch" showControls={false} />
3. CUSTOM COLORS:
<KineticTypography
text="Hello"
colors={{ primary: "#1f2937", accent: "#ff006e" }}
/>
4. SLOWER GLITCH: Change "kt-glitch 0.8s" to "kt-glitch 1.5s"
5. BIGGER BOUNCE: Change "translateY(-20px)" to "translateY(-40px)"
6. FASTER GRADIENT: Change "kt-gradientFlow 4s" to "kt-gradientFlow 2s"
================================================================================
SOURCE CODE
================================================================================
*/
"use client";
import React, { useEffect, useState } from "react";
export type AnimationStyle = "glitch" | "bounce" | "gradient" | "magnify";
export interface KineticTypographyProps {
text?: string;
style?: AnimationStyle;
staggerDelay?: number;
hoverScale?: number;
hoverRotation?: number;
colors?: {
primary?: string;
accent?: string;
secondary?: string;
};
fontSize?: string;
className?: string;
showControls?: boolean;
}
export default function KineticTypography({
text = "AI Native Components",
style: styleProp = "glitch",
staggerDelay = 0.05,
hoverScale = 1.2,
hoverRotation = 10,
colors = {
primary: "#ffffff",
accent: "#ff006e",
secondary: "#8338ec",
},
fontSize = "clamp(2rem, 8vw, 5rem)",
className = "",
showControls = true,
}: KineticTypographyProps) {
const [internalStyle, setInternalStyle] = useState<AnimationStyle>(styleProp);
const [key, setKey] = useState(0);
useEffect(() => {
if (!showControls) setInternalStyle(styleProp);
}, [styleProp, showControls]);
const currentStyle = showControls ? internalStyle : styleProp;
const styles: AnimationStyle[] = ["glitch", "bounce", "gradient", "magnify"];
const handleStyleChange = (newStyle: AnimationStyle) => {
setInternalStyle(newStyle);
if (newStyle === "bounce") setKey((k) => k + 1);
};
const words = text.split(" ");
let charIndex = 0;
return (
<div className={className}>
<style jsx global>{`
/* ====== BASE STYLES ====== */
.kinetic-char {
display: inline-block;
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.kinetic-word {
display: inline-block;
margin: 0 0.15em;
}
/* ====== GLITCH ====== */
.style-glitch {
cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 24 24' fill='%23FFD700' stroke='%23FFD700' stroke-width='1'%3E%3Cpath d='M13 2L4 14h7l-2 8 9-12h-7l2-8z'/%3E%3C/svg%3E") 12 12, pointer;
}
.style-glitch .kinetic-char {
animation: kt-glitch 0.8s infinite; /* GLITCH SPEED */
position: relative;
}
.style-glitch .kinetic-char:hover {
animation: kt-glitch-intense 0.1s infinite; /* GLITCH HOVER SPEED */
}
@keyframes kt-glitch {
0%, 85%, 100% { transform: translate(0); }
87% { transform: translate(2px, -2px); }
90% { transform: translate(-2px, 2px); }
93% { transform: translate(3px, 1px); }
96% { transform: translate(-1px, -3px); }
}
@keyframes kt-glitch-intense {
0% { transform: translate(0); }
20% { transform: translate(-4px, 3px); }
40% { transform: translate(4px, -3px); }
60% { transform: translate(-3px, -2px); }
80% { transform: translate(3px, 2px); }
100% { transform: translate(0); }
}
/* ====== BOUNCE ====== */
.style-bounce {
cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='32' viewBox='0 0 28 32'%3E%3Cpath fill='%23F7931E' d='M14 2c3 4 10 10 10 18 0 6-4 10-10 10S4 26 4 20C4 12 11 6 14 2z'/%3E%3Cpath fill='%23FCEE21' d='M14 8c2 3 6 7 6 12 0 4-2.5 7-6 7s-6-3-6-7c0-5 4-9 6-12z'/%3E%3Cpath fill='%23FBB03B' d='M7 18c0 2 1 5 3 6.5C9 23 8 21 8 19c0-3 2-6 4-8-3 2-5 4-5 7z'/%3E%3C/svg%3E") 14 28, pointer;
}
.style-bounce .kinetic-char {
transition: none;
}
.style-bounce .kinetic-char:hover {
animation: kt-bounce 0.33s ease infinite; /* BOUNCE SPEED */
}
@keyframes kt-bounce {
0%, 100% { transform: translateY(0) scale(1); }
30% { transform: translateY(-20px) scale(1.1); } /* BOUNCE HEIGHT */
50% { transform: translateY(0) scale(0.95); }
70% { transform: translateY(-10px) scale(1.05); }
85% { transform: translateY(0) scale(0.98); }
}
/* ====== GRADIENT ====== */
.style-gradient {
cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28' viewBox='0 0 24 24' fill='%238338ec'%3E%3Cpath d='M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z'/%3E%3C/svg%3E") 14 14, pointer;
}
.style-gradient .kinetic-char {
background: linear-gradient(90deg, #ff006e, #8338ec, #3a86ff, #ff006e); /* GRADIENT COLORS */
background-size: 300% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: kt-gradientFlow 4s ease infinite; /* GRADIENT SPEED */
}
@keyframes kt-gradientFlow {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* ====== MAGNIFY ====== */
.style-magnify {
cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 24 24' fill='none' stroke='%23000000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='10' cy='10' r='7'/%3E%3Cline x1='15' y1='15' x2='21' y2='21'/%3E%3C/svg%3E") 20 20, zoom-in;
}
.style-magnify .kinetic-char {
transition: transform 0.15s ease, font-weight 0.15s ease, text-shadow 0.15s ease, color 0.15s ease; /* MAGNIFY TRANSITION */
}
.style-magnify .kinetic-char:hover {
transform: scale(1.4); /* MAGNIFY SCALE */
font-weight: 900;
text-shadow: 0 0 4px rgba(255, 0, 110, 0.4);
}
`}</style>
<div
key={key}
className={`style-${currentStyle}`}
style={{
fontSize,
fontWeight: 700,
letterSpacing: "-0.02em",
lineHeight: 1.1,
textAlign: "center",
}}
>
{words.map((word, wordIdx) => (
<span key={wordIdx} className="kinetic-word">
{word.split("").map((char, idx) => {
const currentCharIndex = charIndex++;
return (
<span
key={idx}
className="kinetic-char"
data-char={char}
style={{
animationDelay: `${currentCharIndex * staggerDelay}s`,
color: currentStyle !== "gradient" ? colors.primary : undefined,
}}
onMouseEnter={(e) => {
const el = e.currentTarget;
const hasHoverAnimation = ["bounce", "glitch", "magnify"].includes(currentStyle);
if (!hasHoverAnimation) {
el.style.transform = `scale(${hoverScale}) rotate(${hoverRotation}deg)`;
}
if (currentStyle !== "gradient") {
el.style.color = colors.accent || "#ff006e";
}
}}
onMouseLeave={(e) => {
const el = e.currentTarget;
const hasHoverAnimation = ["bounce", "glitch", "magnify"].includes(currentStyle);
if (!hasHoverAnimation) {
el.style.transform = "";
}
if (currentStyle !== "gradient") {
el.style.color = colors.primary || "#ffffff";
}
}}
>
{char}
</span>
);
})}
{wordIdx < words.length - 1 && " "}
</span>
))}
</div>
{showControls && (
<div style={{ marginTop: "2rem", display: "flex", gap: "0.5rem", justifyContent: "center" }}>
{styles.map((s) => (
<button
key={s}
onClick={() => handleStyleChange(s)}
style={{
padding: "0.6rem 1.2rem",
border: "1px solid rgba(255,255,255,0.2)",
background: currentStyle === s ? "#fff" : "rgba(255,255,255,0.05)",
color: currentStyle === s ? "#0a0a0f" : "#fff",
borderRadius: "8px",
cursor: "pointer",
fontSize: "0.875rem",
textTransform: "capitalize",
}}
>
{s}
</button>
))}
</div>
)}
</div>
);
}