- Published on
- Authors
- Name
- Seb Burrell
- @Hankanman
Making Static Content Come Alive
When I started building my blog, I wanted it to stand out. Most developer blogs are static and lifeless - just words on a screen. I envisioned something more dynamic, something that would engage readers from the moment they landed on my site. That's when I decided to create TokenizedText
- a React component that brings text to life through fluid, typewriter-style animations.
The Vision
I had three main goals for this component:
- Create smooth, natural-feeling text animations
- Ensure it was fully responsive and accessible
- Make it easily reusable across my blog
The result is a versatile component that animates text rendering word by word, with a dynamic cursor that adapts to any font size or style. Let's explore what it can do.
Basic Usage
The simplest use case is animating a single line of text:
Show Code
<TokenizedText
key={demoKey}
text="This is the default animation speed with default settings."
typingSpeed={50}
/>
Delayed Animation
You can add dramatic effect by controlling the timing:
Show Code
<TokenizedText
key={demoKey}
text="This text reveals much slower, creating a more dramatic effect..."
typingSpeed={150}
initialCursorDelay={2000}
/>
Sequential Animations
Chain multiple animations together using the onComplete callback:
Show Code
const [step, setStep] = useState(1)
const nextStep = useCallback(() => setStep(prev => prev + 1), [])
{step >= 1 && (
<TokenizedText text="First, this text appears..." typingSpeed={50} onComplete={nextStep} />
)}
{step >= 2 && <TokenizedText text="Then this follows..." typingSpeed={50} onComplete={nextStep} />}
{step >= 3 && <TokenizedText text="And finally, this appears!" typingSpeed={50} />}
Font Size Adaptation
The component automatically adapts to different font sizes:
Show Code
<div>
<div className="mb-4 text-2xl">
<TokenizedText
text="This text is larger!"
typingSpeed={50}
/>
</div>
<div className="text-sm">
<TokenizedText
text="And this text is smaller..."
typingSpeed={50}
/>
</div>
</div>
Styling Options
Combine with Tailwind classes for endless styling possibilities:
Show Code
<div key={demoKey} className="space-y-4">
<div className="font-bold text-primary-500">
<TokenizedText text="This text is bold and uses your primary theme color!" typingSpeed={50} />
</div>
<div className="text-xl italic">
<TokenizedText text="Italicized larger text..." typingSpeed={50} />
</div>
<div className="rounded-lg bg-gray-100 p-4 dark:bg-gray-800">
<TokenizedText
text="Text in a styled container with padding and rounded corners."
typingSpeed={50}
/>
</div>
</div>
Technical Details
Let's look at some of the key technical features that make this component special:
Dynamic Cursor Sizing
One of the trickiest parts was creating a cursor that perfectly matches text height regardless of font size or styling:
const [cursorDimensions, setCursorDimensions] = useState({
width: 0,
height: 0,
})
useEffect(() => {
const updateCursorSize = () => {
if (measureRef.current) {
const computedStyle = window.getComputedStyle(measureRef.current)
const lineHeight = parseFloat(computedStyle.lineHeight)
const fontSize = parseFloat(computedStyle.fontSize)
setCursorDimensions({
width: Math.max(fontSize * 0.4, 2),
height: !isNaN(lineHeight) ? lineHeight : fontSize * 1.2,
})
}
}
updateCursorSize()
const resizeObserver = new ResizeObserver(updateCursorSize)
if (measureRef.current) {
resizeObserver.observe(measureRef.current)
}
return () => resizeObserver.disconnect()
}, [])
Fluid Height Transitions
Managing height transitions smoothly required careful state management and CSS:
const [height, setHeight] = useState<number>(0)
useEffect(() => {
if (visibleTextRef.current) {
const newHeight = visibleTextRef.current.offsetHeight
setHeight(newHeight)
}
}, [text, tokens])
// In the render:
<div
className="overflow-hidden transition-height duration-300 ease-in-out"
style={{
height: `${height}px`,
whiteSpace: 'pre-wrap',
}}
>
Props API
The component exposes a simple but powerful API:
interface TokenizedTextProps {
text: string // The text to animate
typingSpeed?: number // Speed of text appearance (ms)
delay?: number // Initial delay before animation starts
initialCursorDelay?: number // Delay before text starts after cursor appears
className?: string // Additional CSS classes
onComplete?: () => void // Callback when animation completes
}
Use Cases
I've found numerous ways to use this component throughout my blog:
- Hero Sections: Create engaging page introductions
- Section Headers: Add emphasis to important content divisions
- Code Demonstrations: Animate code examples as they're explained
- Interactive Tutorials: Guide users through step-by-step processes
Future Improvements
I'm actively working on several enhancements:
- Add support for custom easing functions
- Implement pause/resume functionality
- Add support for HTML content within the text
- Create preset animation patterns
- Add RTL text support
Getting Started
The component is now available on npm:
npm install react-tokenized-text
Basic usage is straightforward:
import { TokenizedText } from 'react-tokenized-text'
function Welcome() {
return (
<TokenizedText
text="Hello, welcome to my blog!"
typingSpeed={50}
onComplete={() => console.log('Animation complete!')}
/>
)
}
Conclusion
The source code is available on GitHub, and I welcome contributions and feature suggestions. Feel free to reach out if you have any questions or ideas for improvement!