After 20 years of writing JavaScript, I've seen many changes - from callback hell to async/await. But the upcoming JavaScript features will transform how we write code completely.
We've tested these proposals using transpilers and polyfills, ans the results are impressive: code that took 30 lines now takes 10, complex logic becomes readable at a glance, and even junior developers (and Devin 😅) can understand complex parts of our codebase.
If you've read my previous articles on HTML5 elements you didn't know you need or CSS modal windows, you know we like unusual tech at Lingo.dev. These upcoming JavaScript features solve real problems that have annoyed developers for years.
Pipeline Operator Improves Code Readability
Complex data transformations in JavaScript often result in deeply nested function calls that are difficult to read and maintain. Developers must trace through nested functions from inside out, jumping between parentheses to understand the flow of data.
The pipeline operator (|>) solves this problem by allowing data to flow through a series of operations in a clear, top-to-bottom manner:
// Instead of this nested mess
const result = saveToDatabase(
validateUser(
normalizeData(
enrichUserProfile(user)
)
)
);
// You'll write this
const result = user
|> enrichUserProfile
|> normalizeData
|> validateUser
|> saveToDatabase;
Let's look at a more complex real-world example. Consider an image processing service that has grown over time:
// Before: Nested function hell
function processImage(image) {
return compressImage(
addWatermark(
optimizeForWeb(
applyFilter(
resizeImage(image, { width: 800, height: 600 }),
'sepia'
)
),
'Copyright 2025'
),
0.8
);
}
// After: Clean, readable pipeline
function processImage(image) {
return image
|> (img) => resizeImage(img, { width: 800, height: 600 })
|> (img) => applyFilter(img, 'sepia')
|> optimizeForWeb
|> (img) => addWatermark(img, 'Copyright 2025')
|> (img) => compressImage(img, 0.8);
}
Here's another practical example showing how the pipeline operator simplifies data processing for analytics:
// Before: Hard to follow the data flow
function analyzeUserData(users) {
return generateReport(
groupByMetric(
filterInactiveUsers(
normalizeUserData(users)
),
'registrationMonth'
)
);
}
// After: Clear data transformation steps
function analyzeUserData(users) {
return users
|> normalizeUserData
|> filterInactiveUsers
|> (data) => groupByMetric(data, 'registrationMonth')
|> generateReport;
}
The pipeline operator also makes it easier to insert debugging or logging between steps:
function processPayment(payment) {
return payment
|> validatePayment
|> (result) => {
console.log(`Validation result: ${JSON.stringify(result)}`);
return result;
}
|> processTransaction
|> (result) => {
console.log(`Transaction result: ${JSON.stringify(result)}`);
return result;
}
|> sendReceipt;
}
How to Use It Today
The pipeline operator is currently at Stage 2 in the TC39 process as of May 2025. While not yet part of the official JavaScript specification, you can start using it today:
With Babel (Generic Setup):
# Install the pipeline operator plugin
npm install --save-dev @babel/plugin-proposal-pipeline-operator
Add to your .babelrc:
{
"plugins": [
["@babel/plugin-proposal-pipeline-operator", { "proposal": "hack", "topicToken": "%"}]
]
}
With Vite:
# Install dependencies
npm install --save-dev vite-plugin-babel @babel/core @babel/plugin-proposal-pipeline-operator
// vite.config.js
import { defineConfig } from 'vite';
import babel from 'vite-plugin-babel';
export default defineConfig({
plugins: [
babel({
babelConfig: {
plugins: [
['@babel/plugin-proposal-pipeline-operator', { proposal: 'hack', topicToken: '%' }]
]
}
})
]
});
With Next.js:
# Install the pipeline operator plugin
npm install --save-dev @babel/plugin-proposal-pipeline-operator
// .babelrc
{
"presets": ["next/babel"],
"plugins": [
["@babel/plugin-proposal-pipeline-operator", { "proposal": "hack", "topicToken": "%" }]
]
}
With tsup:
# Install dependencies
npm install --save-dev tsup @babel/core @babel/plugin-proposal-pipeline-operator
// tsup.config.ts
import { defineConfig } from 'tsup';
import * as babel from '@babel/core';
import fs from 'fs';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
esbuildPlugins: [
{
name: 'babel',
setup(build) {
build.onLoad({ filter: /\.(jsx?|tsx?)$/ }, async (args) => {
const source = await fs.promises.readFile(args.path, 'utf8');
const result = await babel.transformAsync(source, {
filename: args.path,
presets: [
['@babel/preset-env', { targets: 'defaults' }],
'@babel/preset-typescript'
],
plugins: