Text Icons Effect

An interactive React component that adds a dynamic bubble effect, visually tracking cursor movement in real time.

Search Area
Click to Navigate
Video Area
1
// @ts-nocheck
2
'use client';
3
4
import React, { useState } from 'react';
5
import { useMouse } from '@/hooks/use-mouse';
6
import { Edit, Search, Play, Link } from 'lucide-react';
7
8
const TextIconCursor = () => {
9
const [mouseState, ref] = useMouse();
10
const [cursorContent, setCursorContent] = useState(null);
11
12
const icons = {
13
edit: <Edit size={16} />,
14
search: <Search size={16} />,
15
play: <Play size={16} />,
16
link: <Link size={16} />,
17
};
18
19
return (
20
<div className='relative w-full h-full ' ref={ref}>
21
{mouseState.x !== null && mouseState.y !== null && (
22
<div
23
className='fixed pointer-events-none z-50'
24
style={{
25
left: mouseState.x,
26
top: mouseState.y,
27
transform: 'translate(-50%, -50%)',
28
}}
29
>
30
{/* Main cursor */}
31
<div className='w-4 h-4 bg-white rounded-full mix-blend-screen' />
32
33
{/* Text/Icon container */}
34
{cursorContent && (
35
<div
36
className='absolute left-6 top-0 bg-white/90 text-gray-900 px-3 py-1.5 rounded-full whitespace-nowrap flex items-center gap-2 text-sm animate-fade-in'
37
style={{
38
animation: 'fadeIn 0.2s ease-out',
39
}}
40
>
41
{typeof cursorContent === 'string'
42
? cursorContent
43
: icons[cursorContent]}
44
</div>
45
)}
46
</div>
47
)}
48
49
<div className='flex flex-col items-center justify-center h-full gap-6'>
50
<button
51
className='px-6 py-3 bg-white/10 text-white rounded-lg transition-colors'
52
onMouseEnter={() => setCursorContent('edit')}
53
onMouseLeave={() => setCursorContent(null)}
54
>
55
Edit Button
56
</button>
57
58
<div
59
className='px-6 py-3 bg-white/10 text-white rounded-lg cursor-help'
60
onMouseEnter={() => setCursorContent('Click to search')}
61
onMouseLeave={() => setCursorContent(null)}
62
>
63
Search Area
64
</div>
65
66
<a
67
href='#'
68
className='px-6 py-3 bg-white/10 text-white rounded-lg'
69
onMouseEnter={() => setCursorContent('link')}
70
onMouseLeave={() => setCursorContent(null)}
71
>
72
Click to Navigate
73
</a>
74
75
<div
76
className='w-32 h-32 bg-white/10 rounded-lg flex items-center justify-center'
77
onMouseEnter={() => setCursorContent('play')}
78
onMouseLeave={() => setCursorContent(null)}
79
>
80
<span className='text-white'>Video Area</span>
81
</div>
82
</div>
83
84
<style jsx>{`
85
@keyframes fadeIn {
86
from {
87
opacity: 0;
88
transform: translateX(-10px);
89
}
90
to {
91
opacity: 1;
92
transform: translateX(0);
93
}
94
}
95
`}</style>
96
</div>
97
);
98
};
99
100
export default TextIconCursor;
101

Installtion

1
npm install lucide-react

useMouse

1
'use client';
2
import { type RefObject, useLayoutEffect, useRef, useState } from 'react';
3
4
interface MouseState {
5
x: number | null;
6
y: number | null;
7
elementX: number | null;
8
elementY: number | null;
9
elementPositionX: number | null;
10
elementPositionY: number | null;
11
}
12
13
export function useMouse(): [MouseState, RefObject<HTMLDivElement>] {
14
const [state, setState] = useState<MouseState>({
15
x: null,
16
y: null,
17
elementX: null,
18
elementY: null,
19
elementPositionX: null,
20
elementPositionY: null,
21
});
22
23
const ref = useRef<HTMLDivElement | null>(null);
24
25
useLayoutEffect(() => {
26
const handleMouseMove = (event: MouseEvent) => {
27
const newState: Partial<MouseState> = {
28
x: event.pageX,
29
y: event.pageY,
30
};
31
32
if (ref.current instanceof Element) {
33
const { left, top } = ref.current.getBoundingClientRect();
34
const elementPositionX = left + window.scrollX;
35
const elementPositionY = top + window.scrollY;
36
const elementX = event.pageX - elementPositionX;
37
const elementY = event.pageY - elementPositionY;
38
39
newState.elementX = elementX;
40
newState.elementY = elementY;
41
newState.elementPositionX = elementPositionX;
42
newState.elementPositionY = elementPositionY;
43
}
44
45
setState((s) => ({
46
...s,
47
...newState,
48
}));
49
};
50
51
document.addEventListener('mousemove', handleMouseMove);
52
53
return () => {
54
document.removeEventListener('mousemove', handleMouseMove);
55
};
56
}, []);
57
58
return [state, ref];
59
}

Props

PropTypeDefaultDescription
textstring'Hello!'The text to display next to the cursor.
colorstring'#000'The color of the text and the dot.
fontstring'Arial, sans-serif'The font of the text.
textSizenumber14The size of the text in pixels.
gapnumber5The gap between the dot and the text in pixels.
elementHTMLElementundefinedThe HTML element where the cursor effect will be applied. If not specified, the effect applies to the document.
sizenumber10The size of the dot in pixels.