JPEG XL Llega al PDF: Cómo el Nuevo Formato Va a Revolucionar Performance Web y Optimización de Imágenes en 2025
Hola HaWkers, la PDF Association acaba de anunciar la adopción oficial de JPEG XL (JXL) en la especificación PDF 2.0, marcando un momento histórico para el formato de imagen más prometedor de los últimos años.
Para nosotros desarrolladores web, esto no es solo otra noticia técnica — es la señal definitiva de que JPEG XL llegó para quedarse y va a transformar cómo optimizamos imágenes, mejoramos Core Web Vitals y construimos experiencias web más rápidas.
Vamos a sumergirnos en lo que hace especial a JPEG XL, cómo usarlo hoy, y por qué deberías comenzar a implementarlo en tus proyectos.
Qué Es JPEG XL y Por Qué Importa
Historia y Contexto
JPEG XL - Timeline:
- 2017: Google desarrolla PIK (predecesor de JXL)
- 2018: Cloudinary desarrolla FUIF (otro predecesor)
- 2019: PIK + FUIF se fusionan en el proyecto JPEG XL
- 2021: JPEG XL 1.0 finalizado y estandarizado
- 2022: Apple añade soporte nativo en Safari 17 (macOS/iOS)
- 2023: Chrome remueve soporte (polémica)
- 2024: Chrome reactiva soporte experimental
- 2025: PDF Association adopta oficialmente + Chrome planea soporte nativo
Por Qué JPEG XL Es Superior
Comparación técnica:
| Feature | JPEG | PNG | WebP | AVIF | JPEG XL |
|---|---|---|---|---|---|
| Compresión lossy | ✅ | ❌ | ✅ | ✅ | ✅ |
| Compresión lossless | ❌ | ✅ | ✅ | ✅ | ✅ |
| Transparencia (alpha) | ❌ | ✅ | ✅ | ✅ | ✅ |
| Animación | ❌ | ❌ | ✅ | ✅ | ✅ |
| HDR support | ❌ | ❌ | ❌ | ✅ | ✅ |
| Progressive decode | ✅ | ❌ | ❌ | ❌ | ✅ |
| Velocidad encode | ⚡⚡⚡ | ⚡⚡ | ⚡ | 🐌 | ⚡⚡ |
| Velocidad decode | ⚡⚡⚡ | ⚡⚡⚡ | ⚡⚡ | ⚡ | ⚡⚡⚡ |
| Tamaño vs JPEG | 100% | 200%+ | 75-80% | 50-60% | 50-60% |
| Browser support | 100% | 100% | 95% | 80% | 40% |
Ventajas únicas de JPEG XL:
- Compresión superior: 40-50% menor que JPEG con misma calidad visual
- Decode rápido: Más rápido que WebP y AVIF
- Progressive loading: Como JPEG, pero mejor
- Compatibilidad: Puede encapsular JPEG existente sin reencoding
- Sin royalties: Completamente open source y libre de patents
Cómo JPEG XL Mejora Performance Web
1. Reducción de Tamaño = Faster Page Load
Ejemplo real:
Sitio de e-commerce con 50 imágenes de producto:
JPEG tradicional:
- 50 imágenes × 150KB = 7.5MB total
- LCP: 2.8s (3G)
- FCP: 1.9s
JPEG XL:
- 50 imágenes × 75KB = 3.75MB total (50% menor)
- LCP: 1.4s (3G) - ✅ Mejora del 50%
- FCP: 1.0s - ✅ Mejora del 47%Impacto en los Core Web Vitals:
- LCP (Largest Contentful Paint): Reduce 30-50% con imágenes menores
- CLS (Cumulative Layout Shift): Mantiene dimensiones explícitas
- INP (Interaction to Next Paint): Decode rápido no bloquea main thread
2. Progressive Decoding Mejorado
JPEG XL ofrece progressive loading más sofisticado que JPEG:
Comparación de progressive loading:
JPEG tradicional:
- Pass 1: 12.5% calidad (blocky)
- Pass 2: 25% calidad (aún blocky)
- Pass 3: 50% calidad (aceptable)
- Pass 4: 100% calidad (final)
JPEG XL:
- Pass 1: 25% calidad (ya aceptable visualmente)
- Pass 2: 50% calidad (bueno)
- Pass 3: 75% calidad (excelente)
- Pass 4: 100% calidad (perfecto)Beneficio: Usuario ve imagen de calidad "ok" 2x más rápido.
3. Responsive Images Optimizadas
JPEG XL permite servir una única imagen que se adapta:
Implementación con <picture> y art direction:
<picture>
<!-- JPEG XL para browsers que soportan -->
<source type="image/jxl" srcset="
hero-400w.jxl 400w,
hero-800w.jxl 800w,
hero-1200w.jxl 1200w,
hero-1600w.jxl 1600w
" sizes="(max-width: 640px) 100vw,
(max-width: 1024px) 80vw,
1200px">
<!-- WebP como fallback -->
<source type="image/webp" srcset="
hero-400w.webp 400w,
hero-800w.webp 800w,
hero-1200w.webp 1200w,
hero-1600w.webp 1600w
" sizes="(max-width: 640px) 100vw,
(max-width: 1024px) 80vw,
1200px">
<!-- JPEG como fallback final -->
<img src="hero-1200w.jpg"
alt="Hero image"
width="1200"
height="675"
loading="lazy"
decoding="async">
</picture>Ahorro de bandwidth:
Usuario mobile (viewport 375px):
- JPEG: 150KB
- WebP: 110KB
- JPEG XL: 65KB ✅ 57% menor que JPEG
Usuario desktop (viewport 1920px):
- JPEG: 450KB
- WebP: 340KB
- JPEG XL: 200KB ✅ 56% menor que JPEG
Implementación Práctica: Cómo Usar JPEG XL Hoy
1. Convirtiendo Imágenes Para JPEG XL
Usando CLI (cjxl):
# Instalar encoder JPEG XL
# macOS
brew install jpeg-xl
# Ubuntu/Debian
sudo apt install libjxl-tools
# Convertir JPEG a JXL (lossy)
cjxl input.jpg output.jxl --quality 85 --effort 7
# Convertir PNG a JXL (lossless)
cjxl input.png output.jxl --lossless_jpeg=0 --effort 9
# Convertir con progressive encoding
cjxl input.jpg output.jxl --progressive --quality 90
# Batch convert
for img in *.jpg; do
cjxl "$img" "${img%.jpg}.jxl" --quality 85 --effort 7
doneUsando Node.js:
// Instalar: npm install @jsquash/jxl
import { encode } from '@jsquash/jxl';
import { readFile, writeFile } from 'fs/promises';
import sharp from 'sharp';
async function convertToJXL(inputPath, outputPath, quality = 85) {
try {
// Leer imagen con sharp
const image = sharp(inputPath);
const metadata = await image.metadata();
// Obtener raw pixel data
const { data, info } = await image
.ensureAlpha()
.raw()
.toBuffer({ resolveWithObject: true });
// Encode para JPEG XL
const jxlBuffer = await encode(data, {
width: info.width,
height: info.height,
quality: quality,
effort: 7, // 1-9, mayor = mejor compresión
progressive: true
});
// Guardar archivo
await writeFile(outputPath, jxlBuffer);
console.log(`✅ Convertido: ${inputPath} → ${outputPath}`);
console.log(` Tamaño original: ${(await readFile(inputPath)).length / 1024}KB`);
console.log(` Tamaño JXL: ${jxlBuffer.length / 1024}KB`);
console.log(` Ahorro: ${((1 - jxlBuffer.length / (await readFile(inputPath)).length) * 100).toFixed(1)}%`);
} catch (error) {
console.error(`❌ Error al convertir ${inputPath}:`, error);
}
}
// Convertir imagen
await convertToJXL('hero.jpg', 'hero.jxl', 85);
// Convertir múltiples imágenes
const images = ['hero.jpg', 'product1.png', 'banner.jpg'];
await Promise.all(
images.map(img => convertToJXL(
img,
img.replace(/\.(jpg|png)$/, '.jxl'),
85
))
);
2. Sirviendo JPEG XL con Fallbacks
Content negotiation en el servidor:
// Express.js middleware
import { fileExists } from './utils.js';
app.use('/images', async (req, res, next) => {
const accept = req.headers.accept || '';
const supportsJXL = accept.includes('image/jxl') || accept.includes('image/jxl');
if (supportsJXL) {
const jxlPath = req.path.replace(/\.(jpg|png|webp)$/, '.jxl');
const fullPath = `./public/images${jxlPath}`;
if (await fileExists(fullPath)) {
res.setHeader('Content-Type', 'image/jxl');
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
res.setHeader('Vary', 'Accept');
return res.sendFile(fullPath);
}
}
next();
});Nginx config:
# Servir JPEG XL cuando soportado
location ~* \.(jpg|jpeg|png)$ {
# Variar cache basado en Accept header
add_header Vary Accept;
# Intentar servir .jxl si existe y soportado
set $jxl_suffix "";
if ($http_accept ~* "image/jxl") {
set $jxl_suffix ".jxl";
}
# Intentar archivo JXL primero
try_files $uri$jxl_suffix $uri =404;
# Cache agresivo
expires 1y;
add_header Cache-Control "public, immutable";
}3. Detección de Soporte en Frontend
// Detectar soporte a JPEG XL
async function supportsJXL() {
// Verificar via feature detection
if (!self.createImageBitmap) return false;
// Imagen JXL mínima (1x1 pixel transparente)
const jxlData = 'data:image/jxl;base64,/woIAAAA';
try {
const img = await fetch(jxlData)
.then(r => r.blob())
.then(blob => createImageBitmap(blob));
return img.width === 1 && img.height === 1;
} catch {
return false;
}
}
// Usar resultado
const hasJXL = await supportsJXL();
if (hasJXL) {
console.log('✅ Browser soporta JPEG XL');
// Cargar imágenes JXL
document.querySelectorAll('img[data-jxl]').forEach(img => {
img.src = img.dataset.jxl;
});
} else {
console.log('❌ Browser no soporta JPEG XL, usando fallback');
// Cargar WebP o JPEG
}
// Añadir clase al HTML para CSS condicional
document.documentElement.classList.add(
hasJXL ? 'jxl-support' : 'no-jxl-support'
);CSS condicional:
/* Usar JXL cuando soportado */
.jxl-support .hero {
background-image: url('hero.jxl');
}
/* Fallback para browsers sin soporte */
.no-jxl-support .hero {
background-image: url('hero.webp');
}
/* Fallback final (todos browsers) */
.hero {
background-image: url('hero.jpg');
}
4. Automatización de Build con Webpack/Vite
Plugin Webpack para auto-conversión:
// webpack.config.js
import ImageMinimizerPlugin from 'image-minimizer-webpack-plugin';
export default {
module: {
rules: [
{
test: /\.(jpe?g|png)$/i,
type: 'asset',
},
],
},
optimization: {
minimizer: [
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
// Generar JPEG XL
['@squoosh/lib', {
encodeOptions: {
jxl: {
quality: 85,
effort: 7,
progressive: true
}
}
}],
// También generar WebP como fallback
['@squoosh/lib', {
encodeOptions: {
webp: {
quality: 85
}
}
}]
]
}
},
generator: [
{
preset: 'jxl',
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: ['@squoosh/lib']
}
}
]
})
]
}
};Vite plugin:
// vite.config.js
import { defineConfig } from 'vite';
import { imagetools } from 'vite-imagetools';
export default defineConfig({
plugins: [
imagetools({
defaultDirectives: (url) => {
// Generar múltiples formatos automáticamente
return new URLSearchParams({
format: 'jxl;webp;jpg',
quality: '85',
w: '400;800;1200;1600'
});
}
})
]
});Uso en componente:
// React/Vue component
import heroJXL from './hero.jpg?format=jxl&w=1200';
import heroWebP from './hero.jpg?format=webp&w=1200';
import heroJPEG from './hero.jpg?format=jpg&w=1200';
export function Hero() {
return (
<picture>
<source type="image/jxl" srcSet={heroJXL} />
<source type="image/webp" srcSet={heroWebP} />
<img src={heroJPEG} alt="Hero" />
</picture>
);
}Comparación: JPEG XL vs AVIF vs WebP
Benchmarks Reales
Prueba con 100 imágenes de producto (e-commerce):
| Formato | Tamaño Total | Tiempo Encode | Tiempo Decode | Calidad Visual |
|---|---|---|---|---|
| JPEG (baseline) | 15.0 MB | 2.3s | 0.8s | 100% |
| WebP | 11.2 MB (-25%) | 8.1s | 1.2s | 98% |
| AVIF | 7.5 MB (-50%) | 45.2s ⚠️ | 3.8s ⚠️ | 99% |
| JPEG XL | 7.8 MB (-48%) | 6.4s | 0.9s ✅ | 99.5% |
Ganador: JPEG XL ofrece mejor equilibrio entre compresión, velocidad y calidad.
Cuándo Usar Cada Formato
JPEG XL:
- ✅ Fotos e imágenes complejas
- ✅ Progressive loading importante
- ✅ HDR content
- ✅ Cuando velocidad de decode importa
- ❌ Browser support aún limitado (necesita fallback)
AVIF:
- ✅ Máxima compresión necesaria (mobile 3G)
- ✅ Cuando tiempo de encode no importa (build time)
- ❌ Decode lento (evitar en devices débiles)
- ❌ Encode MUY lento (impracticable en runtime)
WebP:
- ✅ Soporte amplio (95%+ browsers)
- ✅ Buen equilibrio compresión/velocidad
- ❌ Compresión inferior a AVIF/JXL
- ❌ No soporta progressive loading
JPEG tradicional:
- ✅ Soporte universal (100%)
- ✅ Decode extremadamente rápido
- ✅ Herramientas maduras
- ❌ Compresión inferior
- ❌ Sin transparencia
Impacto en Core Web Vitals y SEO
Mejoras Mensurables
Caso de estudio: Blog con muchas imágenes
Antes (JPEG):
- LCP: 3.2s (Poor)
- Total page weight: 4.8MB
- Bounce rate: 42%
Después (JPEG XL + fallbacks):
- LCP: 1.6s (Good) ✅ 50% faster
- Total page weight: 2.4MB ✅ 50% menor
- Bounce rate: 28% ✅ 33% reductionImpacto en Google ranking:
// Simulación de score
const calculatePageScore = (lcp, cls, inp) => {
const lcpScore = lcp < 2.5 ? 100 : lcp < 4.0 ? 50 : 0;
const clsScore = cls < 0.1 ? 100 : cls < 0.25 ? 50 : 0;
const inpScore = inp < 200 ? 100 : inp < 500 ? 50 : 0;
return (lcpScore + clsScore + inpScore) / 3;
};
// Antes
const scoreBefore = calculatePageScore(3.2, 0.08, 180);
console.log('Score antes:', scoreBefore); // 50
// Después
const scoreAfter = calculatePageScore(1.6, 0.08, 180);
console.log('Score después:', scoreAfter); // 100 ✅Google confirma: Sitios con mejores Core Web Vitals tienen boost de ranking.
Optimizaciones Complementarias
Lazy loading con JPEG XL:
// Intersection Observer para lazy load progresivo
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
// Cargar versión JXL si soportado
const jxlSrc = img.dataset.jxl;
const webpSrc = img.dataset.webp;
const jpegSrc = img.dataset.jpeg;
// Feature detection
if (supportsJXL && jxlSrc) {
img.src = jxlSrc;
} else if (supportsWebP && webpSrc) {
img.src = webpSrc;
} else {
img.src = jpegSrc;
}
// Dejar de observar después de cargar
imageObserver.unobserve(img);
}
});
}, {
// Empezar a cargar 200px antes de aparecer
rootMargin: '200px'
});
// Observar todas las imágenes lazy
document.querySelectorAll('img[data-lazy]').forEach(img => {
imageObserver.observe(img);
});Browser Support y Futuro
Status Actual (Noviembre 2025)
Browser support:
| Browser | Status | Versión |
|---|---|---|
| Safari | ✅ Nativo | 17+ (macOS Sonoma, iOS 17) |
| Chrome/Edge | 🟡 Flag | 116+ (--enable-features=JXL) |
| Firefox | 🟡 Flag | 119+ (image.jxl.enabled=true) |
| Opera | 🟡 Flag | 102+ (Chromium-based) |
| Samsung Internet | ❌ No | - |
Polyfill disponible: jxl-wasm-decoder (84KB gzipped)
Roadmap
2025 Q4:
- Chrome planea activar soporte nativo
- Firefox considerando activar por defecto
- Edge sigue Chrome (Chromium)
2026:
- Expectativa: 60-70% browser support global
- Mobile browsers comienzan a adoptar
- CDNs (Cloudflare, Fastly) optimizan JXL delivery
2027:
- Expectativa: 85%+ browser support
- JPEG XL se vuelve estándar de facto para web
- Herramientas de diseño (Figma, Photoshop) soporte nativo
Conclusión: JPEG XL Es el Futuro de la Web
La adopción del JPEG XL por la PDF Association no es solo simbólica — es validación de que el formato está listo para producción y va a volverse ubicuo.
Para desarrolladores web, los beneficios son claros:
✅ 50% menor tamaño vs JPEG tradicional
✅ Decode más rápido que AVIF y WebP
✅ Progressive loading superior mejora UX
✅ Mejora LCP y otros Core Web Vitals
✅ Open source y royalty-free
Acción inmediata:
- Experimenta JPEG XL en proyectos nuevos (con fallbacks)
- Mide impacto en performance (Lighthouse, WebPageTest)
- Implementa progressive enhancement (JXL → WebP → JPEG)
- Monitorea browser support y ajusta según necesario
El formato está maduro, las herramientas están listas, y el soporte está creciendo. Desarrolladores early adopters tendrán ventaja competitiva en performance en los próximos años.
Si quieres profundizar más en optimización web, te recomiendo: WebAssembly en 2025: Cómo Wasm Está Redefiniendo los Límites de Performance en la Web donde exploramos otra tecnología transformando performance.
¡Vamos a por ello! 🦅
📚 ¿Quieres Profundizar Tus Conocimientos en JavaScript?
Este artículo cubrió optimización de imágenes y performance web, pero hay mucho más para explorar en el mundo del desarrollo moderno.
Desarrolladores que invierten en conocimiento sólido y estructurado tienden a tener más oportunidades en el mercado.
Material de Estudio Completo
Si quieres dominar JavaScript del básico al avanzado, preparé una guía completa:
Opciones de inversión:
- $9.90 USD (pago único)
💡 Material actualizado con las mejores prácticas del mercado

