SwiftUI-Style Text Transitions for the Web – NumericText

Category: Animation , Javascript , Text | June 18, 2026
Authorshizukushq
Last UpdateJune 18, 2026
LicenseMIT
Views0 views
SwiftUI-Style Text Transitions for the Web – NumericText

NumericText is a lightweight, framework-agnostic web component that animates text transitions between strings and numbers.

The library was designed to recreate the behavior and feel of SwiftUI’s .numericText() for the browser.

You can use it in vanilla JavaScript projects, install framework wrappers for React, Vue, Svelte, or Solid, or import the core package from an ESM CDN in browser-first webpages.

See It In Action:

Features:

  • Animate numbers and arbitrary text.
  • Run in vanilla JavaScript projects.
  • Support React, Vue, Svelte, and Solid wrappers.
  • Avoid third-party runtime dependencies.
  • Keep the core package small.
  • Respect reduced-motion preferences.
  • Support RTL interface text.
  • Preserve accessible screen reader labels.

Use Cases:

  • Live dashboards show changing KPI values with less visual jump.
  • Pricing tables highlight plan changes during billing updates.
  • Scoreboards update player totals while keeping digits readable.
  • Command palettes swap status labels in compact UI space.

How To Use It:

Installation

Install the core package for vanilla JavaScript.

npm install @numeric-text/core

Install a framework wrapper when your project renders UI through a component tree.

npm install @numeric-text/react
npm install @numeric-text/vue
npm install @numeric-text/svelte
npm install @numeric-text/solid

You can also import the core package from an ESM CDN for browser use.

<script type="module">
  import 'https://esm.sh/@numeric-text/core';
</script>

Basic Usage

Add the custom element to your HTML, import the core package, then update the element value through JavaScript.

<numeric-text
  id="checkoutTotal"
  role="img"
  aria-label="Checkout total is 118 dollars"
></numeric-text>
<script type="module">
  // Registers the <numeric-text> custom element.
  import '@numeric-text/core';
  const checkoutTotal = document.querySelector('#checkoutTotal');
  // Set the first value instantly.
  checkoutTotal.value = 'Total $118';
  // Animate the next text change.
  setTimeout(() => {
    checkoutTotal.setAttribute('aria-label', 'Checkout total is 124 dollars');
    checkoutTotal.update('Total $124');
  }, 1200);
</script>

The value property sets the displayed content. The update() method changes the content and animates the transition by default.

Advanced Usages

Dashboard refreshes often need instant replacement before fresh data arrives. The second argument turns animation off for a single vanilla JavaScript update.

const orderCount = document.querySelector('#orderCount');
// Replace the value instantly.
orderCount.update('Orders 1,240', false);

Financial interfaces sometimes need all digits to move downward during negative changes. The snippet sets a fixed downward direction before the next animated update.

const priceChange = document.querySelector('#priceChange');
// Force the transition direction downward.
priceChange.setOptions({
  trend: -1,
});
priceChange.update('$48.20');

Ticker labels may need shorter motion than dashboard metrics. The snippet changes duration and easing for one element instance.

const stockLabel = document.querySelector('#stockLabel');
// Replace the default timing.
stockLabel.setOptions({
  transition: {
    duration: 320,
    easing: 'ease-out',
  },
});
stockLabel.update('AAPL 216.42');

React Implementation

import { useState } from 'react';
import NumericText from '@numeric-text/react';
export default function CartSummary() {
  const [total, setTotal] = useState(86);
  return (
    <button onClick={() => setTotal(total + 12)}>
      <NumericText value={`Cart total $${total}`} />
    </button>
  );
}

Vue Implementation

<script setup lang="ts">
import { ref } from 'vue';
import NumericText from '@numeric-text/vue';
const seatsLeft = ref(18);
function reserveSeat() {
  seatsLeft.value -= 1;
}
</script>
<template>
  <button @click="reserveSeat">
    <NumericText :value="`${seatsLeft} seats left`" />
  </button>
</template>

Svelte projects can pass a reactive variable into the wrapper. The text transition runs when the value changes.

<script lang="ts">
  import NumericText from '@numeric-text/svelte';
  let buildStatus = 'Queued';
</script>
<button on:click={() => (buildStatus = 'Running')}>
  <NumericText value={buildStatus} />
</button>

All configuration options

  • value (string | number): Sets the text or number displayed by the component.
  • animated (boolean): Controls animation in framework wrappers. For vanilla JavaScript, pass false as the second argument to update().
  • trend (1 | 0 | -1): Controls animation direction. Use 1 for upward motion, -1 for downward motion, or 0 for automatic numeric direction.
  • transition (Transition): Sets animation timing with a duration and easing function.
  • respectMotionPreference (boolean): Disables animation when the user has enabled reduced motion in system settings.

API Methods

const metricText = document.querySelector('#metricText');
// Display a new value with the default animated transition.
metricText.update('Revenue +24%');
// Display a new value instantly in vanilla JavaScript.
metricText.update('Revenue +31%', false);
// Change animation timing, direction, and motion preference handling.
metricText.setOptions({
  trend: 1,
  transition: {
    duration: 400,
    easing: 'ease-out',
  },
  respectMotionPreference: true,
});

Alternatives:

FAQs:

Q: Does NumericText animate only numbers?
A: No. NumericText supports numbers and regular text strings. It works well for short labels, metric values, status text, prices, and counters.

Q: Why does my HTML attribute not update the text?
A: Set the value property or call update() on the element. The vanilla JavaScript API works through the custom element instance.

Q: Why does my text wrap or lose typographic details?
A: NumericText targets single-line UI text. It renders characters in separate spans, so multiline layout, ligatures, and kerning are not supported.

Q: What happens if I pass a value that contains special characters or emojis?
A: NumericText uses Intl.Segmenter with granularity: 'grapheme', so multi-codepoint emojis and combined characters are treated as a single unit. The animation and diffing remain correct across all Unicode scripts.

You Might Be Interested In:


Leave a Reply