Transformers.js: Revolutionizing NLP in Your Browser
Le brief IA que les pros lisent chaque soir
Les 7 actus IA du jour, décryptées en 5 min. Gratuit.
Inclus dès l'inscription : notre sélection des meilleurs guides & comparatifs IA.
Choisis ton rythme
Gratuit · Pas de spam · Désabonnement en 1 clic
A New Era for Natural Language Processing with Transformers.js
For a long time, running transformer models for natural language processing (NLP) required complex infrastructure. Users often had to maintain a Python server, invest in costly GPU time, and manage inference requests via an API. This meant that every user input left their device, passed through remote infrastructure, and returned as a prediction. This approach was justified at a time when models were too large to run locally. However, that era is coming to an end.
Transformers.js marks a decisive turning point. This library allows users to run state-of-the-art NLP models directly in their browser, without the need for a server. Models are downloaded once, cached locally, and can then operate offline. The conversion from Python code to JavaScript is almost direct:
import { pipeline } from '@huggingface/transformers';
const classifier = await pipeline('sentiment-analysis');
const result = await classifier('I love transformers!');
This tutorial explores three NLP tasks: text classification, zero-shot labeling, and question answering, using the pipeline() API from Transformers.js. For each task, we will see how to initialize the pipeline, interpret the output structure, and provide a functional HTML example that you can test directly in your browser. The tutorial concludes with a complete support ticket routing application, integrating all three pipelines into a handy tool.
Each code example uses the CDN import path, eliminating the need for any build step. Just open a text editor, paste the code, and run it.
What is Transformers.js?
The Transformers.js library aims to be functionally equivalent to the Python transformers library from Hugging Face. This means it uses the same pre-trained models, the same task names, and the same pipeline API, but in JavaScript. The secret to this compatibility lies in ONNX Runtime.
Models initially trained in PyTorch, TensorFlow, or JAX are converted to the ONNX format using Hugging Face Optimum. ONNX Runtime then executes these models in the browser. By default, it runs on the CPU via WebAssembly (WASM), compatible with all modern browsers. For those seeking GPU acceleration, the device: 'webgpu' option allows the use of the browser's WebGPU API, providing significantly increased execution speed where possible, although this feature remains experimental in some environments.
Model Caching. When a pipeline is first executed, the model weights are downloaded from the Hugging Face Hub and cached in the browser's IndexedDB or in the file system in Node.js. Tests show that the sentiment analysis pipeline downloads about 111 MB on the first load. Subsequent executions are done directly from the cache, making the process fast and functional offline.
Quantization. The dtype option controls the model's precision. q8 (8-bit quantization) is the default for WASM, offering a good balance between size and precision. q4 reduces the file size by about half, with a precision loss of 1 to 3% on most tasks, which is ideal for mobile or slow connections. For server-side use in Node.js, fp32 ensures full precision without size constraints.
// Default WASM execution -- works everywhere
const pipe = await pipeline('sentiment-analysis');
// WebGPU for faster inference on compatible hardware
const pipe = await pipeline('sentiment-analysis', null, { device: 'webgpu' });
// 4-bit quantization for smaller model downloads
const pipe = await pipeline('sentiment-analysis', 'Xenova/distilbert-base-uncased-finetuned-sst-2-english');
The pipeline() API
The pipeline function is the main interface for most use cases. It combines a pre-trained model, a tokenizer, and post-processing logic into a single callable object. You do not need to manipulate the tokenizer or model weights directly. You simply call the pipeline with text and receive a structured output.
The function signature consists of three parts:
const pipe = await pipeline(task, model?, options?);
const result = await pipe(input, inferenceOptions?);
- task is a string identifier that tells the library what type of model to load and how to handle input and output.
- model is optional; if you omit it, the library loads the default model for that task. If you specify a model ID (like 'Xenova/distilbert-base-uncased-finetuned-sst-2-english'), that model is loaded from the Hub.
- options is where you define
device,dtype, andprogress_callback.
Both steps are asynchronous. pipeline() downloads and loads the model into memory, which is the slow part during the first execution. The pipe call is generally quick once the model is loaded. Both return Promises, meaning your user interface must handle the loading state.
A progress_callback allows you to track the download and display progress to the user:
// progress_callback is triggered during model download with status updates
const pipe = await pipeline(
'sentiment-analysis',
'Xenova/distilbert-base-uncased-finetuned-sst-2-english',
progress_callback: (progress) => {
// progress.status can be: 'initiate', 'download', 'progress', 'done'
if (progress.status === 'progress') {
const pct = Math.round(progress.progress);
document.getElementById('progress').textContent =
`Loading model: ${pct}%`;
}
if (progress.status === 'ready') {
document.getElementById('progress').textContent = 'Model ready';
}
}
);
It is important to note that Transformers.js is an inference-only library. You cannot fine-tune or train models with it. If your task requires a custom model, training must be done elsewhere (Python, cloud), and the resulting ONNX export runs in the browser.
Task 1: Text Classification
Text classification assigns a label and a confidence score to the input text. The most common form is sentiment analysis, positive versus negative, but the same pipeline architecture handles any fixed set of categories the model has been trained on.
What the output looks like:
const result = await classifier('This product completely exceeded my expectations.');
// [{ label: 'POSITIVE', score: 0.9997 }]
The output is an array of objects. Each object has label (the predicted class as a string) and score (a float between 0 and 1 representing the model's confidence). A score of 0.9997 means the model is very confident. A score of 0.52 means it is barely above the decision threshold; consider this uncertain and handle it accordingly in your application logic.
The output is always an array, even for a single input, as the same pipeline call handles batches:
const results = await classifier([
'This is great!',
'Completely broken, waste of money.'
]);
// { label: 'POSITIVE', score: 0.9998 },
// { label: 'NEGATIVE', score: 0.9991 }
Complete Functional Example
The example below is a complete, standalone HTML file. Open it in any modern browser. The model downloads on the first execution and caches subsequent loads, which are instantaneous.
<html lang="en">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Text Classification with Transformers.js</title>
<body style="font-family: system-ui, sans-serif; max-width: 680px; margin: 2rem auto; padding: 0 1rem;">
<textarea id="input" placeholder="Enter text to classify..."></textarea>
<button id="classify-btn" disabled>Loading model...</button>
<div id="status">Downloading model on first run (this may take a moment)...</div>
<div id="result"></div>
<script type="module">
import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.0.2';
const statusEl = document.getElementById('status');
const resultEl = document.getElementById('result');
const btn = document.getElementById('classify-btn');
const inputEl = document.getElementById('input');
async function loadModel() {
classifier = await pipeline(
'text-classification',
'Xenova/distilbert-base-uncased-finetuned-sst-2-english',
progress_callback: (p) => {
if (p.status === 'progress') {
const pct = Math.round(p.progress ?? 0);
statusEl.textContent = `Loading model: ${pct}%`;
btn.textContent = 'Classify';
btn.disabled = false;
statusEl.textContent = 'Model loaded and cached. Subsequent loads are instantaneous.';
}
}
);
}
async function classify() {
const text = inputEl.value.trim();
if (!text) return;
btn.disabled = true;
btn.textContent = 'Classifying...';
resultEl.textContent = '';
const results = await classifier(text);
const { label, score } = results[0];
const pct = (score * 100).toFixed(1);
const cssClass = label === 'POSITIVE' ? 'positive' : 'negative';
resultEl.innerHTML = `<span class="${cssClass}">${label}</span> -- ${pct}% confidence`;
btn.disabled = false;
btn.textContent = 'Classify';
}
btn.addEventListener('click', classify);
loadModel().catch(err => {
statusEl.textContent = `Error loading model: ${err.message}`;
});
</script>
</body>
</html>
The loadModel function calls pipeline() with the task name, model ID, and options. The progress_callback is triggered multiple times during the download and updates the status text so the user does not stare at a frozen screen. Once the model is loaded, the button is enabled. When the user clicks Classify, classifier(text) performs inference synchronously from the cache, typically in under 200 ms on a modern laptop. The result destructures label and score from the first element of the array, formats the confidence as a percentage, and applies a CSS class for color coding.
Task 2: Zero-Shot Classification
Zero-shot classification does something that regular text classification cannot do: it classifies text into categories that you define at runtime, with no training data required. You pass in the text and a list of possible labels, and the model predicts the likelihood that each label is correct. This allows for enormous flexibility, especially for applications where categories may frequently vary or are not known in advance.
The output for zero-shot classification is similar to that of text classification, but it includes scores for each proposed label, allowing you to determine which is the most likely. This ability to adapt to dynamic categories without requiring retraining is a major asset for developers looking to integrate advanced NLP features into their applications without heavy maintenance burdens.
Brief IA — L'actualité IA en français
L'essentiel de l'actualité de l'intelligence artificielle, décrypté et expliqué chaque jour.