Touchspin
Touch-friendly numeric spinner for React, Tailwind, and vanilla JavaScript
Install / Use
/learn @istvan-ujjmeszaros/TouchspinREADME
TouchSpin v5
Formerly Bootstrap TouchSpin — Now a modern ESM-first monorepo
TouchSpin (formerly Bootstrap TouchSpin) is a modern rewrite of the popular spinner component. The v5 line ships as an ESM-first monorepo with framework-specific packages so you can pick the delivery mode that fits your stack—core logic, renderer bundles, a jQuery bridge, or a Web Component.
💖 Support This Project
TouchSpin v5 represents nearly 1,000 hours of development work — a complete ground-up rewrite to bring modern ESM architecture, tree-shaking, and multi-framework support to this popular component.
⭐ Become a Sponsor to help sustain ongoing development, faster bug fixes, and new features.
Your sponsorship keeps this project:
- ✅ Free and open-source for everyone
- 🚀 Actively maintained with regular updates
- 🐛 Well-tested with comprehensive test coverage
- 📚 Well-documented with migration guides
Every contribution, no matter the size, makes a real difference!
Packages at a Glance
| Package | Purpose | Primary Entry | Bundled Assets |
|---------|---------|---------------|----------------|
| @touchspin/core | Framework-agnostic logic + renderer contracts | dist/index.js (ESM) | Declarations only |
| @touchspin/standalone | Standalone mount API (core + renderer) | dist/index.js (ESM) | Per-renderer subpaths (dist/bootstrapX.js) |
| @touchspin/jquery | Drop-in jQuery wrapper | dist/index.js (ESM) | dist/umd/jquery.touchspin-*.umd.js globals |
| @touchspin/webcomponent | <touchspin-input> custom element | Per-renderer subpaths | dist/umd/*.touchspin.umd.js globals |
| @touchspin/renderer-bootstrap3 | Bootstrap 3 renderer + CSS | dist/index.js (ESM) | dist/touchspin-bootstrap3.css |
| @touchspin/renderer-bootstrap4 | Bootstrap 4 renderer + CSS | dist/index.js (ESM) | dist/touchspin-bootstrap4.css |
| @touchspin/renderer-bootstrap5 | Bootstrap 5 renderer + CSS | dist/index.js (ESM) | dist/touchspin-bootstrap5.css |
| @touchspin/renderer-tailwind | Tailwind-friendly renderer | dist/index.js (ESM) | dist/touchspin-tailwind.css |
| @touchspin/renderer-vanilla | Framework-free renderer + theme | dist/index.js (ESM) | dist/touchspin-vanilla.css, dist/themes/vanilla.css |
All packages declare "type": "module", target Node 22 (the configuration used for builds), and include licenses in the published tarballs. Renderer packages list their CSS under files and expose the stylesheet via exports."./css".
Framework Adapters (Separate Repositories)
Framework-specific adapters are maintained in separate repositories with native tooling:
-
@touchspin/react (v5.0.1-alpha.0)
- Repository: https://github.com/istvan-ujjmeszaros/touchspin-react
- Installation:
npm install @touchspin/react@alpha @touchspin/core@alpha - Per-renderer subpath imports with controlled/uncontrolled patterns
-
@touchspin/angular (v5.0.1-alpha.0)
- Repository: https://github.com/istvan-ujjmeszaros/touchspin-angular
- Installation:
npm install @touchspin/angular@alpha @touchspin/core@alpha - ControlValueAccessor integration with Angular forms
-
@touchspin/vue (v5.1.0)
- Repository: https://github.com/istvan-ujjmeszaros/touchspin-vue
- Installation:
npm install @touchspin/vue @touchspin/core - Per-renderer subpath imports with v-model support
-
@touchspin/svelte (v5.0.0)
- Repository: https://github.com/istvan-ujjmeszaros/touchspin-svelte
- Installation:
npm install @touchspin/svelte @touchspin/core - Svelte 5 Runes support with bind:value directive
These adapters are independently versioned to match core compatibility. React and Angular are currently in alpha while Vue and Svelte are stable.
Quick Install
Standalone Adapter (Recommended)
The simplest way to use TouchSpin with a mount API:
npm install @touchspin/standalone
import { mount } from '@touchspin/standalone/bootstrap5';
const api = mount('#quantity', {
min: 0,
max: 100,
step: 1,
cancelable: true // Enable cancelable change events
});
// Listen to events
const input = document.querySelector('#quantity');
input.addEventListener('change:start', (event) => {
// Prevent change if needed
if (someCondition) {
event.preventDefault();
}
});
input.addEventListener('speedchange', (event) => {
console.log('Speed changed to:', event.detail.speed);
});
Browser via CDN (ESM):
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@touchspin/renderer-bootstrap5@5/dist/touchspin-bootstrap5.css">
<script type="module">
import { mount } from 'https://cdn.jsdelivr.net/npm/@touchspin/standalone@5/dist/bootstrap5.js';
mount('#quantity', { min: 0, max: 100 });
</script>
Modern (ESM) projects
For advanced use with direct core access:
npm install @touchspin/core @touchspin/renderer-bootstrap5
import { TouchSpin } from '@touchspin/core';
import Bootstrap5Renderer from '@touchspin/renderer-bootstrap5';
import '@touchspin/renderer-bootstrap5/css';
const input = document.querySelector('#quantity');
TouchSpin(input, {
renderer: Bootstrap5Renderer,
min: 0,
max: 100,
step: 1,
cancelable: true // Enable cancelable change events
});
// Listen to events
input.addEventListener('change:start', (event) => {
// Can prevent the change
event.preventDefault();
});
jQuery integration
npm install @touchspin/jquery jquery
UMD (Browser):
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@touchspin/jquery@5/dist/umd/jquery.touchspin-bootstrap5.umd.js"></script>
<script>
// Canonical (recommended)
$('#quantity').touchspin({ min: 0, max: 100 });
// Legacy alias (still supported)
$('#quantity').TouchSpin({ min: 0, max: 100 });
</script>
ESM:
import { autoInstall } from '@touchspin/jquery';
import { mount } from '@touchspin/standalone/bootstrap5';
import $ from 'jquery';
autoInstall(mount);
$('#quantity').touchspin({ min: 0, max: 100 });
Web Component
npm install @touchspin/webcomponent
import '@touchspin/webcomponent/bootstrap5';
<touchspin-input min="0" max="100" value="42"></touchspin-input>
React
npm install @touchspin/react react react-dom
Controlled:
import { useState } from 'react';
import TouchSpin from '@touchspin/react/bootstrap5';
function App() {
const [value, setValue] = useState(50);
return <TouchSpin value={value} onChange={setValue} min={0} max={100} />;
}
Uncontrolled:
import TouchSpin from '@touchspin/react/vanilla';
<TouchSpin defaultValue={25} onChange={(val) => console.log(val)} />
Imperative API:
import { useRef } from 'react';
import TouchSpin from '@touchspin/react/tailwind';
import type { TouchSpinHandle } from '@touchspin/react/tailwind';
const ref = useRef<TouchSpinHandle>(null);
<TouchSpin ref={ref} defaultValue={10} />
ref.current?.increment();
Per-renderer imports: bootstrap3, bootstrap4, bootstrap5, tailwind, vanilla
SSR-safe: Works with Next.js, Remix, and other React frameworks
Example app: touchspin-react-example
See the @touchspin/react repository for complete API documentation.
Angular
npm install @touchspin/angular @angular/core @angular/common @angular/forms
Template-driven forms:
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TouchSpinBootstrap5Component } from '@touchspin/angular/bootstrap5';
@Component({
selector: 'app-example',
standalone: true,
imports: [FormsModule, TouchSpinBootstrap5Component],
template: `
<touch-spin
[(ngModel)]="quantity"
[min]="0"
[max]="100"
[step]="1"
></touch-spin>
`
})
export class ExampleComponent {
quantity = 50;
}
Reactive forms:
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { TouchSpinBootstrap5Component } from '@touchspin/angular/bootstrap5';
@Component({
selector: 'app-example',
standalone: true,
imports: [ReactiveFormsModule, TouchSpinBootstrap5Component],
template: `<touch-spin [formControl]="amountControl"></touch-spin>`
})
export class ExampleComponent {
amountControl = new FormControl(50);
}
Imperative API:
import { Component, ViewChild } from '@angular/core';
import { TouchSpinBootstrap5Component, TouchSpinHandle } from '@touchspin/angular/bootstrap5';
@Component({
selector: 'app-example',
standalone: true,
imports: [TouchSpinBootstrap5Component],
template: `<touch-spin #spinner [(ngModel)]="value"></touch-spin>`
})
export class ExampleComponent {
@ViewChild('spinner') spinner?: TouchSpinHandle;
value = 0;
increment() {
this.spinner?.increment();
}
}
Per-renderer imports: bootstrap3, bootstrap4, bootstrap5, tailwind, vanilla
ControlValueAccessor: Full integration with Angular forms
SSR-safe: Compatible with Angular Universal
See the @touchspin/angular repository for complete API documentation.
CDN Builds
All CDN-facing entry points are catalogued in docs/cdn-assets.md. Remember that most packages are ESM-only; only the jQuery and Web
