Glitch Button
Cyberpunk-inspired buttons with glitch effects that can be triggered on hover or click.
Always Glitching
ACTIVE
ACTIVE
ACTIVE
ACTIVE
Custom Colors
nuvyx UI
Download Button
DOWNLOAD
SHOW INTERACTIVE DEMO
Installation Guide
1
Install Dependencies
Tailwind Merge
npm install tailwind-merge
2
Copy Component Code
Glitch Button.tsx
TypeScript1"use client";
2import React, { useState, useEffect, useRef } from "react";
3import { twMerge } from "tailwind-merge";
4
5interface GlitchButtonProps {
6 children: React.ReactNode;
7 className?: string;
8 onClick?: () => void;
9 glitchIntensity?: "low" | "medium" | "high";
10 glitchOnHover?: boolean;
11 glitchAlways?: boolean;
12 glitchColors?: {
13 primary?: string;
14 secondary?: string;
15 };
16 borderColor?: string;
17}
18
19export const GlitchButton: React.FC<GlitchButtonProps> = ({
20 children,
21 className = "",
22 onClick,
23 glitchIntensity = "medium",
24 glitchOnHover = true,
25 glitchAlways = false,
26 glitchColors = {
27 primary: "#ef00ef",
28 secondary: "#00ffff",
29 },
30 borderColor = "white",
31}) => {
32 const [isHovering, setIsHovering] = useState(false);
33 const [isClicked, setIsClicked] = useState(false);
34 const buttonRef = useRef<HTMLDivElement>(null);
35 const [bgColorClass, setBgColorClass] = useState("bg-gray-900");
36 const [textColorClass, setTextColorClass] = useState("text-white");
37 const [isRounded, setIsRounded] = useState(false);
38
39 useEffect(() => {
40 const bgMatch = className.match(/bg-[a-z]+-[0-9]+/);
41 if (bgMatch) {
42 setBgColorClass(bgMatch[0]);
43 }
44 const textMatch = className.match(/text-[a-z]+-[0-9]+/);
45 if (textMatch) {
46 setTextColorClass(textMatch[0]);
47 }
48 setIsRounded(
49 className.includes("rounded") ||
50 className.includes("rounded-lg") ||
51 className.includes("rounded-md") ||
52 className.includes("rounded-sm") ||
53 className.includes("rounded-full")
54 );
55 }, [className]);
56
57 useEffect(() => {
58 const intensityMultiplier =
59 glitchIntensity === "low" ? 0.5 : glitchIntensity === "high" ? 2 : 1;
60
61 const styleId = "glitch-button-styles";
62 if (!document.getElementById(styleId)) {
63 const styleTag = document.createElement("style");
64 styleTag.id = styleId;
65 styleTag.innerHTML = `
66 @keyframes glitchFx1 {
67 0%, 100% { transform: translateX(0); }
68 10% { transform: translateX(0); }
69 20% { transform: translateX(${-4 * intensityMultiplier}px); }
70 30% { transform: translateX(0); }
71 40% { transform: translateX(${12 * intensityMultiplier}px); }
72 50% { transform: translateX(${8 * intensityMultiplier}px); }
73 60% { transform: translateX(${24 * intensityMultiplier}px); }
74 70% { transform: translateX(${2 * intensityMultiplier}px); }
75 80% { transform: translateX(${-4 * intensityMultiplier}px); }
76 90% { transform: translateX(0); }
77 }
78 @keyframes glitchFx2 {
79 0% { transform: translateX(${4 * intensityMultiplier}px); }
80 10% { transform: translateX(${-12 * intensityMultiplier}px); }
81 20% { transform: translateX(${-2 * intensityMultiplier}px); }
82 30% { transform: translateX(${1 * intensityMultiplier}px); }
83 40%, 50%, 60%, 70% { transform: translateX(0); }
84 80% { transform: translateX(${4 * intensityMultiplier}px); }
85 90% { transform: translateX(${-2 * intensityMultiplier}px); }
86 100% { transform: translateX(${-15 * intensityMultiplier}px); }
87 }
88 @keyframes glitchFx3 {
89 0% { transform: translateX(${4 * intensityMultiplier}px); }
90 10% { transform: translateX(0); }
91 20% { transform: translateX(${4 * intensityMultiplier}px); }
92 30% { transform: translateX(0); }
93 40% { transform: translateX(${-4 * intensityMultiplier}px); }
94 50% { transform: translateX(0); }
95 60% { transform: translateX(${-4 * intensityMultiplier}px); }
96 70% { transform: translateX(0); }
97 80% { transform: translateX(${12 * intensityMultiplier}px); }
98 90% { transform: translateX(0); }
99 100% { transform: translateX(${-12 * intensityMultiplier}px); }
100 }
101
102 @keyframes glitchSkew {
103 0% { transform: skew(0deg); }
104 10% { transform: skew(${1 * intensityMultiplier}deg); }
105 20% { transform: skew(${-2 * intensityMultiplier}deg); }
106 30% { transform: skew(${1.5 * intensityMultiplier}deg); }
107 40% { transform: skew(${-1 * intensityMultiplier}deg); }
108 50% { transform: skew(${2 * intensityMultiplier}deg); }
109 60% { transform: skew(${-1.5 * intensityMultiplier}deg); }
110 70% { transform: skew(${1.5 * intensityMultiplier}deg); }
111 80% { transform: skew(${-2 * intensityMultiplier}deg); }
112 90% { transform: skew(${1 * intensityMultiplier}deg); }
113 100% { transform: skew(0deg); }
114 }
115
116 @keyframes flickerAnimation {
117 0% { opacity: 1; }
118 19% { opacity: 1; }
119 20% { opacity: 0; }
120 21% { opacity: 1; }
121 49% { opacity: 1; }
122 50% { opacity: 0.7; }
123 51% { opacity: 1; }
124 79% { opacity: 1; }
125 80% { opacity: 0.8; }
126 81% { opacity: 1; }
127 100% { opacity: 1; }
128 }
129
130 @keyframes clickGlitch {
131 0%, 100% { clip-path: inset(0 0 0 0); }
132 20% { clip-path: inset(${20 * intensityMultiplier}% 0 ${
133 40 * intensityMultiplier
134 }% 0); }
135 40% { clip-path: inset(${50 * intensityMultiplier}% 0 ${
136 20 * intensityMultiplier
137 }% 0); }
138 60% { clip-path: inset(${30 * intensityMultiplier}% 0 ${
139 60 * intensityMultiplier
140 }% 0); }
141 80% { clip-path: inset(${10 * intensityMultiplier}% 0 ${
142 70 * intensityMultiplier
143 }% 0); }
144 }
145
146 .glitch-layer-1 {
147 animation: glitchFx1 ${800 / intensityMultiplier}ms infinite step-end;
148 }
149 .glitch-layer-2 {
150 animation: glitchFx2 ${900 / intensityMultiplier}ms infinite step-end;
151 }
152 .glitch-layer-3 {
153 animation: glitchFx3 ${
154 1000 / intensityMultiplier
155 }ms infinite step-end;
156 }
157 .glitch-skew {
158 animation: glitchSkew ${
159 1200 / intensityMultiplier
160 }ms infinite step-end;
161 }
162 .flicker-animation {
163 animation: flickerAnimation 2s infinite;
164 }
165 .click-glitch {
166 animation: clickGlitch 500ms step-end forwards;
167 }
168 `;
169 document.head.appendChild(styleTag);
170 }
171 }, [glitchIntensity]);
172
173 const handleMouseEnter = () => {
174 setIsHovering(true);
175 };
176
177 const handleMouseLeave = () => {
178 setIsHovering(false);
179 };
180
181 const handleClick = () => {
182 if (onClick) {
183 onClick();
184 }
185 setIsClicked(true);
186 setTimeout(() => setIsClicked(false), 500);
187 };
188
189 const showGlitch = glitchAlways || (glitchOnHover && isHovering) || isClicked;
190
191 const borderStyle = {
192 boxShadow: `inset ${borderColor} 5px 5px 0px, inset ${borderColor} -5px 5px 0px, inset ${borderColor} 5px -5px 0px, inset ${borderColor} -5px -5px 0px`,
193 };
194
195 const containerClasses = twMerge(
196 "relative cursor-pointer font-mono overflow-hidden",
197 "text-4xl",
198 "bg-gray-900",
199 "text-white",
200 "p-4 m-1.5",
201 "group",
202 "hover:shadow-none",
203 "hover:bg-gradient-to-r hover:from-fuchsia-600 hover:via-white hover:to-cyan-400",
204 isClicked ? "click-glitch" : "",
205 className
206 );
207
208 const textShadowStyle = {
209 textShadow: `
210 -1.5px -1.5px 0 ${glitchColors.primary},
211 1.5px 1.5px 0 ${glitchColors.secondary}
212 `,
213 };
214
215 const glitchLayerShadowTop = {
216 boxShadow: `inset ${borderColor} 5px 5px 0px, inset ${borderColor} -5px 0px 0px`,
217 };
218
219 const glitchLayerShadowMiddle = {
220 boxShadow: `inset ${borderColor} 5px 0px 0px, inset ${borderColor} -5px 0px 0px`,
221 };
222
223 const glitchLayerShadowBottom = {
224 boxShadow: `inset ${borderColor} 5px -5px 0px, inset ${borderColor} -5px 0px 0px`,
225 };
226
227 return (
228 <div
229 ref={buttonRef}
230 onClick={handleClick}
231 onMouseEnter={handleMouseEnter}
232 onMouseLeave={handleMouseLeave}
233 className={containerClasses}
234 style={borderStyle}
235 >
236 <span
237 className={`
238 block
239 ${showGlitch ? "opacity-0" : "opacity-100"}
240 transition-opacity
241 ${glitchAlways ? "flicker-animation" : ""}
242 `}
243 >
244 {children}
245 </span>
246
247 {showGlitch && (
248 <div
249 className={`
250 absolute inset-0
251 overflow-hidden
252 ${isClicked ? "glitch-skew" : ""}
253 `}
254 >
255 <div
256 className={`
257 absolute left-0 w-full h-1/3 top-0
258 ${bgColorClass}
259 ${isRounded ? "rounded-t-lg" : ""}
260 overflow-hidden
261 glitch-layer-1
262 `}
263 style={glitchLayerShadowTop}
264 >
265 <div
266 className={`
267 absolute w-full ${textColorClass}
268 top-0 left-0 right-0
269 flex items-center justify-center
270 h-full
271 `}
272 style={textShadowStyle}
273 >
274 <div className="transform translate-y-0 mt-[45px]">
275 {children}
276 </div>
277 </div>
278 </div>
279
280 <div
281 className={`
282 absolute left-0 w-full h-1/3 top-1/3
283 ${bgColorClass}
284 overflow-hidden
285 glitch-layer-2
286 `}
287 style={glitchLayerShadowMiddle}
288 >
289 <div
290 className={`
291 absolute w-full ${textColorClass}
292 top-0 left-0 right-0
293 flex items-center justify-center
294 h-[300%] -translate-y-1/3
295 `}
296 style={textShadowStyle}
297 >
298 <div>{children}</div>
299 </div>
300 </div>
301
302 <div
303 className={`
304 absolute left-0 w-full h-1/3 top-2/3
305 ${bgColorClass}
306 ${isRounded ? "rounded-b-lg" : ""}
307 overflow-hidden
308 glitch-layer-3
309 `}
310 style={glitchLayerShadowBottom}
311 >
312 <div
313 className={`
314 absolute w-full ${textColorClass}
315 top-0 left-0 right-0
316 flex items-center justify-center
317 h-[300%] -translate-y-2/3
318 `}
319 style={textShadowStyle}
320 >
321 <div>{children}</div>
322 </div>
323 </div>
324 </div>
325 )}
326 </div>
327 );
328};
329
3
Final Steps
Update Import Paths
Make sure to update the import paths in the component code to match your project structure. For example, change @/components/ui/button
to match your UI components location.
Props
Name | Type | Default | Description |
---|---|---|---|
glitchIntensity | string | "medium" | The intensity of the glitch effect. Possible values: "low", "medium", "high". |
glitchOnHover | boolean | true | Whether to trigger glitch effects when hovering over the button. |
borderColor | string | "white" | Custom border color for the glitch effect. |
glitchAlways | boolean | false | Whether to continuously display the glitch effect regardless of hover/click state. |
glitchColors | object | { primary: '#ef00ef', secondary: '#00ffff' } | Custom colors for the glitch effect. The object should include properties: primary and secondary. |
className | string | "" | Additional CSS classes to apply. Background and text colors are detected from these classes. |
onClick | function | undefined | Function to call when the button is clicked. |
children | React.ReactNode | undefined | Content of the button. |
Examples
SYSTEM ACCESS
SYSTEM ACTIVE
SYSTEM ACTIVE
SYSTEM ACTIVE
SYSTEM ACTIVE
DOWNLOAD
WARNING
SUBSCRIBE
ENTER THE VOID
MORE INFO