SecretoCho
menu

component 05 / 28WORKING

SC Typewriter Title

type
Code Component
platform
Framer
last update
license
SecretoCho Design
needs JavaScript

demo · runs with JavaScript

fig 00 · live demo, ported from SC_TypewriterTitle.tsx

Type & erase rotating phrases next to a fixed title—fast to configure, accessible, and performant.

A configurable typewriter headline for Framer. Displays a fixed lead-in plus a rotating list of phrases, typed and erased with a blinking caret. Tunable typing speed, hold time, caret style, layout (inline/stacked), and full typography controls.

  • UI
  • Typography
  • Text
  • Motion

Main

Overview

Built With
React (hooks), CSS (inline), Framer Code Component API
Layout Support
Responsive; inline or vertical stacking
Orientation
Text-only, left-to-right sequence
Ideal Use Case
Hero headings, landing page taglines, product slogans

Tags

  • typewriter
  • headline
  • caret
  • animated text
  • rotating phrases

Code comment

/* * SC Typewriter Title * © SecretoCho Design 2025 (https://secretocho.design) * A flexible headline that types & deletes rotating phrases with a blinking caret. * Time-based state machine using setTimeout/setInterval; no heavy libs. * @framerDisableUnlink * @framerIntrinsicWidth 480 * @framerIntrinsicHeight 120 */

Dependencies

Technologies
None (pure React + timers)
Notes
No external motion libraries; uses setTimeout/setInterval.

Complexity

Logic Level
Medium (typed/deleted state machine, timers, looping)
Runtime Cost
Very Low (string slicing; no RAF)
Component Size
~3–4 KB TSX
Notes
Timers are cleaned on unmount to prevent leaks.

Guide

Instructions

  1. Add to your Framer project and place on the canvas.
  2. Set Fixed Text (mainTitle) and Rotating Phrases (animatedTitles).
  3. Adjust Typing Speed, Display Duration (hold), Initial Delay, and Repeat.
  4. Style with Typography, Fixed/Animated Text Color, and Cursor Style.
  5. Choose Text Arrangement: inline (side-by-side) or stacked (new line).

Examples / Presets

  • Preset A – Default Hero

    “Designs that” + phrases: “convert & captivate” … speed=60ms, hold=1600ms, caret |

  • Preset B – Fast Tagline

    speed
    30ms
    hold
    800ms
    blink
    300ms
    layout
    inline
  • Preset C – Minimal Stacked

    layout=stacked, fontSize=32, caret _, colors: main #111, animated #555

Mechanics

How it Works

  • Maintains index (phrase), subIndex (char position), and deleting flags.
  • Typing/deleting steps scheduled with setTimeout(typeSpeed).
  • After fully typed, waits holdTime then switches to deleting.
  • On reaching 0 chars, advances to next phrase; loops if loop=true.
  • Caret blinking toggled via setInterval(caretBlinkSpeed); blink is CSS opacity.

Performance & Tuning

  • Typing uses setTimeout; no layout thrash; safe for most devices.
  • Smaller typeSpeed = faster typing; tune holdTime for rhythm.
  • Long strings are fine; avoid extremely large phrase arrays for readability.

Compatibility & Limits

  • Modern browsers; no WebGL/RAF required.
  • Uses inline styles; adopt tokens if you need theme-wide control.
  • Text renders as real DOM text (copyable/selectable).

Quality

Accessibility

  • Pure text output; readable by screen readers.
  • Motion sensitivity: consider exposing a toggle or respecting prefers-reduced-motion (pause typing & caret).
  • Ensure color contrast for both fixed and animated text.

Security & Privacy

  • No network calls or storage.
  • No dangerouslySetInnerHTML or eval.
  • All timers are cleared on unmount.

Reference

Props

nametypedefaultdescription
mainTitlestring“Designs that”Static lead-in text that never changes.
animatedTitlesstring“convert & captivate”Array of phrases to type and erase in order.
fontFontStylefamily:“Inter”Typography for both fixed and animated segments.
mainColorcolor”#000000”Color for the fixed text.
animatedColorcolor”#333333”Color for the animated text.
caretCharenum”|”Caret glyph used during typing.
caretBlinkSpeednumber500Blink interval for caret visibility.
typeSpeednumber60Delay between each typed/deleted character.
holdTimenumber1600Time to keep a phrase fully typed before deleting.
startDelaynumber300Initial wait before starting the typing cycle.
loopbooleantrueIf false, stops after the last phrase.
layoutenum“inline”Inline places phrases next to the fixed text; stacked puts them on a new line.

Notes & Tips

  • Shorter, high-impact phrases work best for scanning.
  • For tighter layouts, reduce fontSize and set layout="stacked".
  • To pause after the last phrase (non-loop), set loop=false.

Troubleshooting / FAQ

Caret not blinking

Check caretBlinkSpeed (>0) and that component is mounted.

Animation never starts

Ensure startDelay isn’t too high; confirm animatedTitles isn’t empty.

Stops on last phrase

Expected when loop=false.

Source

Header comment

SC_TypewriterTitle.tsx · lines 4–14 · header comment
/* * TypewriterTitle.tsx – Framer Code Component * ----------------------------------------------------------- * A highly‑configurable headline that reveals an array of sub‑titles * via a typewriter animation + blinking caret. * * © SecretoCho Design – July 2025 * @framerDisableUnlink * @framerIntrinsicWidth 480 * @framerIntrinsicHeight 120 */

props [REF]

Key excerpt

One step function: type to the end, hold, delete to the start, then move to the next phrase; a timer re-arms it at the typing speed.

SC_TypewriterTitle.tsx · lines 65–97 · tsx
    // ---------------------------------------------------------    const step = useCallback(() => {        const phrase = animatedTitles[index] || ""        const atEnd = subIndex === phrase.length        const atStart = subIndex === 0        if (atEnd && !deleting) {            // hold before deleting            typeTimer.current = setTimeout(() => setDeleting(true), holdTime)            return        }        if (atStart && deleting) {            // next phrase / loop            setDeleting(false)            setIndex((prev) => {                const next = prev + 1                if (next < animatedTitles.length) return next                return loop ? 0 : prev            })            return        }        // continue typing or deleting        setSubIndex((prev) => prev + (deleting ? -1 : 1))        typeTimer.current = setTimeout(step, typeSpeed)    }, [animatedTitles, deleting, holdTime, index, loop, subIndex, typeSpeed])    // Start typing    useEffect(() => {        typeTimer.current = setTimeout(step, startDelay)        return () => {            if (typeTimer.current) clearTimeout(typeTimer.current)        }    }, [step, startDelay])

Credits

Credits

Implementation & Framer adaptation by SecretoCho Design.

license
SecretoCho Design

SecretoCho Design 2025

Disclaimer

Proves

Hiring a design engineer?

I’m open to senior design engineer roles: remote from Belgrade, or relocation with visa sponsorship. Write to me, or read the CV first. Client work stays selective, one project at a time.

sc@secretocho.design

read the CV · LinkedIn