JSON Formatter JavaScript — JSON.stringify()

·Front-end & Node.js Developer·Revisado porMarcus Webb·Publicado

Usa el Formateador y Embellecedor JSON gratuito directamente en tu navegador — sin instalación.

Probar Formateador y Embellecedor JSON online →

Cuando depuro respuestas de API en Node.js, un muro de JSON minificado es lo primero que me frena — una llamada a JSON.stringify(data, null, 2) y la estructura se vuelve inmediatamente legible. Para formatear JSON en JavaScript, no necesitas nada más allá del propio entorno de ejecución: JSON.stringify está integrado en todos los navegadores y en Node.js, sin instalación alguna. Si solo necesitas un resultado rápido sin escribir código, el Formateador JSON de ToolDeck lo hace al instante. Esta guía cubre todo lo práctico: el parámetro space, arrays y funciones replacer, el manejo de Date, BigInty referencias circulares, lectura y escritura de archivos JSON en Node.js (18+), formateo desde la línea de comandos, y la librería fast-json-stringify para la serialización en producción.

  • JSON.stringify(data, null, 2) está integrado en todos los navegadores y Node.js — sin instalación.
  • El parámetro replacer acepta un array (lista blanca de claves) o una función (transformar valores) — úsalo para enmascarar campos sensibles.
  • Los objetos Date se serializan automáticamente mediante toJSON() → cadena ISO 8601; BigInt lanza TypeError y requiere un replacer personalizado.
  • Las referencias circulares lanzan TypeError — corrígelo con un Set seen en una función replacer, o usa la librería flatted.
  • Para formatear desde la CLI usa node -p "JSON.stringify(require('./file.json'),null,2)" — sin herramientas adicionales.

¿Qué es el formateo JSON?

El formateo JSON (también llamado pretty-printing) transforma una cadena JSON compacta y minificada en una presentación legible con sangría consistente y saltos de línea. Los datos subyacentes son idénticos — solo cambia el espacio en blanco. El JSON compacto es óptimo para la transferencia por red donde cada byte importa; el JSON formateado es óptimo para depuración, revisión de código e inspección de registros. La función JSON.stringify() de JavaScript maneja ambos casos con una sola llamada al cambiar el parámetro space.

Before · json
After · json
{"orderId":"ord_8f2a91bc","status":"shipped","items":[{"sku":"HDMI-4K-2M","qty":2,"unitPrice":12.99}],"total":25.98}
{
  "orderId": "ord_8f2a91bc",
  "status": "shipped",
  "items": [
    {
      "sku": "HDMI-4K-2M",
      "qty": 2,
      "unitPrice": 12.99
    }
  ],
  "total": 25.98
}

JSON.stringify() — El formateador integrado

JSON.stringify() es una función global en todos los entornos JavaScript — navegadores, Node.js, Deno, Bun — sin necesidad de importación. Su tercer argumento, space, controla la sangría: pasa un número para ese número de espacios por nivel, o la cadena '\t' para tabulaciones. Si se omite (o se pasa null), se obtiene una salida compacta en una sola línea.

JavaScript — ejemplo mínimo funcional
const serverConfig = {
  host: "api.payments.internal",
  port: 8443,
  workers: 4,
  tls: { enabled: true, cert: "/etc/ssl/certs/api.pem" },
  rateLimit: { requestsPerMinute: 1000, burst: 50 }
}

console.log(JSON.stringify(serverConfig, null, 2))
// {
//   "host": "api.payments.internal",
//   "port": 8443,
//   "workers": 4,
//   "tls": {
//     "enabled": true,
//     "cert": "/etc/ssl/certs/api.pem"
//   },
//   "rateLimit": {
//     "requestsPerMinute": 1000,
//     "burst": 50
//   }
// }

El parámetro space acepta un número (1–10 espacios) o una cadena. Pasar un carácter de tabulación produce una salida que muchos editores y herramientas de diff prefieren. También puedes combinar los tres parámetros — aquí un patrón real que uso al escribir JSON formateado en archivos de configuración:

JavaScript — variaciones de space
const telemetryEvent = {
  eventId: "evt_3c7f9a2b",
  service: "checkout-api",
  severity: "warn",
  latencyMs: 342,
  region: "eu-west-1",
  tags: ["payment", "timeout", "retry"]
}

// Sangría de 2 espacios (más común en proyectos JS)
JSON.stringify(telemetryEvent, null, 2)

// Sangría con tabulación (preferida por algunos linters y herramientas de config)
JSON.stringify(telemetryEvent, null, '\t')

// Compacto — sin espacios en blanco (para transferencia por red)
JSON.stringify(telemetryEvent)
// {"eventId":"evt_3c7f9a2b","service":"checkout-api",...}
Nota:Los valores undefined, las funciones y los valores Symbol se omiten silenciosamente de la salida. Si el valor de una propiedad es undefined, esa clave no aparecerá en absoluto en la cadena serializada — es una fuente habitual de errores al registrar objetos con campos opcionales.

Funciones replacer — Filtrar y transformar la salida

El segundo argumento de JSON.stringify() es el replacer. Tiene dos formas: un array que incluye solo las claves indicadas, o una función que se invoca para cada par clave/valor y puede filtrar, transformar o redactar valores. Recurro al array cuando necesito un subconjunto rápido, y a la función cuando necesito enmascarar datos sensibles antes de registrarlos.

Replacer de array — Lista blanca de claves específicas

JavaScript — replacer de array
const order = {
  orderId: "ord_8f2a91bc",
  customer: {
    id: "usr_4421",
    email: "c.mendoza@ejemplo.com",
    passwordHash: "bcrypt:$2b$12$XKzV..."
  },
  items: [{ sku: "HDMI-4K-2M", qty: 2, unitPrice: 12.99 }],
  createdAt: "2026-03-10T14:22:00Z"
}

// Solo incluir campos seguros en el registro
const safeLog = JSON.stringify(order, ["orderId", "items", "createdAt"], 2)
// {
//   "orderId": "ord_8f2a91bc",
//   "items": [{ "sku": "HDMI-4K-2M", "qty": 2, "unitPrice": 12.99 }],
//   "createdAt": "2026-03-10T14:22:00Z"
// }
// passwordHash y customer.email quedan excluidos

Replacer de función — Transformar valores

JavaScript — replacer de función
const auditRecord = {
  requestId: "req_7d2e91",
  user: { id: "usr_4421", email: "c.mendoza@ejemplo.com", apiKey: "sk-live-eKx9..." },
  action: "update_billing",
  timestamp: new Date("2026-03-10T14:22:00Z"),
  durationMs: 87
}

function safeReplacer(key, value) {
  // Redactar campos que parecen secretos o PII
  if (key === "apiKey") return "[REDACTADO]"
  if (key === "email") return value.replace(/(?<=.{2}).+(?=@)/, "***")
  return value
}

console.log(JSON.stringify(auditRecord, safeReplacer, 2))
// {
//   "requestId": "req_7d2e91",
//   "user": { "id": "usr_4421", "email": "c.***@ejemplo.com", "apiKey": "[REDACTADO]" },
//   "action": "update_billing",
//   "timestamp": "2026-03-10T14:22:00.000Z",
//   "durationMs": 87
// }
Nota:La función replacer se invoca con this establecido en el objeto que contiene la clave actual. La primera llamada pasa una cadena vacía como clave y el valor completo que se está serializando como valor — devuélvelo sin cambios para continuar con la serialización normal.

Manejo de tipos no serializables

No todos los valores de JavaScript se corresponden limpiamente con JSON. Conocer el comportamiento de cada tipo evita pérdidas silenciosas de datos y errores inesperados en producción.

Date — Automático mediante toJSON()

Los objetos Date implementan un método toJSON() que devuelve una cadena ISO 8601. JSON.stringify() llama a toJSON() automáticamente antes de serializar, por lo que no se requiere ningún manejo personalizado.

JavaScript — serialización de Date
const webhook = {
  eventType: "payment.succeeded",
  occurredAt: new Date("2026-03-10T14:22:00Z"),
  processedAt: new Date()
}

JSON.stringify(webhook, null, 2)
// {
//   "eventType": "payment.succeeded",
//   "occurredAt": "2026-03-10T14:22:00.000Z",
//   "processedAt": "2026-03-10T14:22:01.347Z"
// }

// Cualquier objeto con un método toJSON() recibe el mismo trato:
const custom = { toJSON: () => "valor-personalizado", hidden: 42 }
JSON.stringify(custom) // '"valor-personalizado"'

Clases personalizadas — Implementar toJSON()

Cualquier clase puede implementar un método toJSON() y JSON.stringify() lo llamará automáticamente durante la serialización. Esto es más limpio que un replacer global para tipos de dominio que aparecen en todo el código.

JavaScript — toJSON() personalizado
class Money {
  constructor(amount, currency) {
    this.amount = amount
    this.currency = currency
  }
  toJSON() {
    // Llamado automáticamente por JSON.stringify
    return { amount: this.amount, currency: this.currency, formatted: `${this.currency} ${this.amount.toFixed(2)}` }
  }
}

class OrderId {
  constructor(id) { this.id = id }
  toJSON() { return this.id }  // Serializar como cadena simple
}

const invoice = {
  invoiceId: new OrderId('inv_8f2a91bc'),
  subtotal: new Money(199.00, 'USD'),
  tax:      new Money(15.92, 'USD'),
  issuedAt: new Date('2026-03-10T14:22:00Z')
}

JSON.stringify(invoice, null, 2)
// {
//   "invoiceId": "inv_8f2a91bc",
//   "subtotal": { "amount": 199, "currency": "USD", "formatted": "USD 199.00" },
//   "tax": { "amount": 15.92, "currency": "USD", "formatted": "USD 15.92" },
//   "issuedAt": "2026-03-10T14:22:00.000Z"
// }
Nota:toJSON() tiene prioridad sobre la función replacer. Si ambos están presentes, toJSON() se ejecuta primero — el replacer recibe el valor ya convertido, no la instancia de clase original.

BigInt — TypeError sin un replacer

JavaScript — replacer para BigInt
// Esto lanza: TypeError: Do not know how to serialize a BigInt
// JSON.stringify({ sessionId: 9007199254741234n })

// Solución: convertir BigInt a string en el replacer
function bigIntReplacer(_key, value) {
  return typeof value === 'bigint' ? value.toString() : value
}

const metrics = {
  requestCount: 9007199254741234n,  // supera Number.MAX_SAFE_INTEGER
  service: "ingestion-worker",
  region: "us-east-1"
}

JSON.stringify(metrics, bigIntReplacer, 2)
// {
//   "requestCount": "9007199254741234",
//   "service": "ingestion-worker",
//   "region": "us-east-1"
// }

Referencias circulares — TypeError sin un Set seen

JavaScript — replacer para referencias circulares
// Esto lanza: TypeError: Converting circular structure to JSON
// const node = { id: "n1" }; node.self = node; JSON.stringify(node)

// Solución: rastrear objetos vistos con un WeakSet
function circularReplacer() {
  const seen = new WeakSet()
  return function (_key, value) {
    if (typeof value === 'object' && value !== null) {
      if (seen.has(value)) return '[Circular]'
      seen.add(value)
    }
    return value
  }
}

const parent = { id: "node_parent", label: "root" }
const child  = { id: "node_child", parent }
parent.child = child  // circular

JSON.stringify(parent, circularReplacer(), 2)
// {
//   "id": "node_parent",
//   "label": "root",
//   "child": { "id": "node_child", "parent": "[Circular]" }
// }

// Alternativa: npm install flatted
// import { stringify } from 'flatted'
// stringify(parent)  // gestiona refs circulares de forma nativa

Referencia de parámetros de JSON.stringify()

Los tres parámetros tienen soporte completo en todos los entornos JavaScript modernos. Los valores por defecto producen JSON compacto en una sola línea — pasa los parámetros explícitamente para obtener una salida legible.

Parámetro
Tipo
Por defecto
Descripción
value
any
El valor a serializar. Se admiten de forma nativa objetos, arrays, cadenas, números, booleanos y null.
replacer
function | Array<string | number> | null
null
Filtra o transforma pares clave/valor. Array = lista blanca de claves a incluir. Función = se invoca para cada clave/valor.
space
number | string | null
null
Sangría. Número = espacios por nivel (máx. 10). Cadena = sangría literal (p. ej. "\t"). null o 0 = salida compacta.

Formatear JSON desde un archivo y una respuesta de API

En Node.js (18+) a menudo necesitas reformatear un archivo de configuración JSON o mostrar con formato una respuesta de API para depuración. Ambos patrones usan el mismo enfoque de dos pasos: analizar el texto crudo con JSON.parse() y luego volver a serializarlo con JSON.stringify().

Leer y reescribir un archivo de configuración JSON

Node.js 18+ — reformatear un archivo JSON (ESM)
import { readFileSync, writeFileSync } from 'fs'

try {
  const raw = readFileSync('./config/database.json', 'utf8')
  const config = JSON.parse(raw)
  writeFileSync('./config/database.json', JSON.stringify(config, null, 2))
  console.log('Config reformateado correctamente')
} catch (err) {
  console.error('Error al reformatear config:', err.message)
  // JSON.parse lanza SyntaxError si el archivo contiene JSON inválido
  // readFileSync lanza ENOENT si el archivo no existe
}

Reformateo asíncrono de archivo (fs/promises)

Node.js 18+ — reformateo asíncrono de archivo
import { readFile, writeFile } from 'fs/promises'

async function reformatJson(filePath) {
  const raw = await readFile(filePath, 'utf8')
  const parsed = JSON.parse(raw)
  const formatted = JSON.stringify(parsed, null, 2)
  await writeFile(filePath, formatted, 'utf8')
  return { keys: Object.keys(parsed).length }
}

// Uso
const { keys } = await reformatJson('./config/feature-flags.json')
console.log(`Reformateadas ${keys} claves de nivel superior`)

Mostrar con formato JSON de una respuesta fetch()

Al construir o depurar un cliente de API en Node.js 18+ o el navegador, formatear el cuerpo de la respuesta es la forma más rápida de entender lo que devolvió el servidor. El patrón estándar es response.json() (objeto analizado) pasado a JSON.stringify(). Si necesitas la cadena cruda primero — por ejemplo para calcular un hash o registrarla literalmente — usa response.text() y luego JSON.parse().

JavaScript — fetch + formato (Node.js 18+ o navegador)
// Patrón 1: response.json() → mostrar con formato
async function debugEndpoint(url) {
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.API_TOKEN}` }
  })
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`)

  const data = await res.json()
  console.log(JSON.stringify(data, null, 2))
}

await debugEndpoint('https://api.stripe.com/v1/charges?limit=3')
JavaScript — response.text() cuando necesitas la cadena cruda primero
// Patrón 2: response.text() → analizar → formatear
// Útil cuando quieres registrar la respuesta cruda Y mostrarla con formato
async function inspectRawResponse(url) {
  const res = await fetch(url)
  const raw = await res.text()

  console.log('Longitud de la respuesta cruda:', raw.length)
  try {
    const parsed = JSON.parse(raw)
    console.log('Formateado:')
    console.log(JSON.stringify(parsed, null, 2))
  } catch {
    console.error('La respuesta no es JSON válido:', raw.slice(0, 200))
  }
}

Formateo desde la línea de comandos

Node.js incluye suficiente capacidad para formatear JSON desde el terminal sin herramientas adicionales. Estos one-liners son útiles en scripts de CI, pipelines de despliegue y alias de shell. Para un uso interactivo aún más rápido, instala jq — el estándar de facto para la manipulación de JSON en línea de comandos.

bash — formatear un archivo JSON con Node.js
# Formatear package.json usando node -p (print)
node -p "JSON.stringify(require('./package.json'), null, 2)"

# Formatear desde stdin (compatible con pipes, funciona en CI)
echo '{"port":3000,"env":"production"}' | node -e "
  const d = require('fs').readFileSync(0, 'utf8')
  console.log(JSON.stringify(JSON.parse(d), null, 2))
"

# Formatear una respuesta de API guardada en un archivo
cat api-response.json | node -e "
  process.stdin.setEncoding('utf8')
  let s = ''
  process.stdin.on('data', c => s += c)
  process.stdin.on('end', () => console.log(JSON.stringify(JSON.parse(s), null, 2)))
"
bash — alternativas universales
# Alternativa con Python (preinstalado en macOS y la mayoría de Linux)
cat api-response.json | python3 -m json.tool

# jq — más rápido y con más funcionalidades (brew install jq / apt install jq)
cat api-response.json | jq .

# jq — formatear y filtrar en un solo paso
cat api-response.json | jq '.data.users[] | {id, email}'
Nota:Al ejecutar Node.js en modo ESM (p. ej. con "type": "module" en package.json), require() no está disponible en one-liners. Usa --input-type=module y fs.readFileSync en su lugar, o cambia a node -e con un fragmento CommonJS como se muestra arriba.

Si no estás en la terminal — pegando una respuesta de Postman o un archivo de logs — el Formateador JSON de ToolDeck te permite pegar, formatear y copiar en un solo paso con resaltado de sintaxis y validación integrada.

Alternativa de alto rendimiento — fast-json-stringify

fast-json-stringify genera una función serializadora dedicada a partir de un JSON Schema. Como conoce la forma de los datos de antemano, puede omitir la verificación de tipos y usar concatenación de cadenas en lugar de descenso recursivo — los benchmarks suelen mostrar una mejora de rendimiento de 2–5× respecto a JSON.stringify() en payloads grandes y repetitivos. Lo uso en rutas de API de alta frecuencia donde el coste de serialización aparece en los perfiles de rendimiento.

bash
npm install fast-json-stringify
JavaScript — fast-json-stringify para eventos de telemetría
import fastJson from 'fast-json-stringify'

const serializeTelemetryEvent = fastJson({
  title: 'TelemetryEvent',
  type: 'object',
  properties: {
    eventId:   { type: 'string' },
    service:   { type: 'string' },
    severity:  { type: 'string', enum: ['info', 'warn', 'error'] },
    latencyMs: { type: 'integer' },
    timestamp: { type: 'string' },
    region:    { type: 'string' }
  },
  required: ['eventId', 'service', 'severity', 'latencyMs', 'timestamp']
})

const event = {
  eventId:   'evt_3c7f9a2b',
  service:   'checkout-api',
  severity:  'warn',
  latencyMs: 342,
  timestamp: new Date().toISOString(),
  region:    'eu-west-1'
}

const json = serializeTelemetryEvent(event)
// '{"eventId":"evt_3c7f9a2b","service":"checkout-api","severity":"warn",...}'
Nota:fast-json-stringify está diseñado para la serialización en producción de datos estructurados — siempre produce salida compacta (sin formato legible). Para salida legible durante el desarrollo, usa JSON.stringify(data, null, 2) con normalidad.

Salida de terminal con resaltado de sintaxis

La función integrada de Node.js util.inspect() produce una salida con colores y legible optimizada para la visualización en terminal. Gestiona las referencias circulares y BigInt de forma nativa, y renderiza recursivamente objetos anidados a cualquier profundidad. La salida no es JSON válido — usa sintaxis JavaScript (p. ej. true en lugar de true, pero renderiza funciones y Symbols en lugar de omitirlos), lo que lo hace ideal para scripts de depuración en Node.js pero no apto para respuestas de API o salida a archivo.

Node.js 18+ — util.inspect con colores
import { inspect } from 'util'

const payload = {
  requestId: "req_7d2e91",
  user: { id: "usr_4421", roles: ["admin", "billing"] },
  metadata: { ipAddress: "203.0.113.42", userAgent: "Mozilla/5.0" },
  createdAt: new Date()
}

// depth: null → expandir todos los niveles anidados; colors: true → colores ANSI de terminal
console.log(inspect(payload, { colors: true, depth: null }))
Aviso:No uses util.inspect() para JSON escrito en archivos, enviado por la red o almacenado en una base de datos. Su salida no es JSON válido y causará errores de análisis en cualquier sistema downstream que llame a JSON.parse() sobre ella. Resérvala exclusivamente para la depuración interactiva en terminal.

Trabajar con archivos JSON grandes

JSON.parse() carga todo el archivo en memoria antes de analizarlo — correcto para payloads pequeños, pero impracticable para archivos de más de 50–100 MB como exportaciones de bases de datos, volcados de registros de aplicaciones o lotes de análisis. Para estos casos, los Streams de Node.js y la librería stream-json permiten procesar registros uno a uno sin saturar el heap.

Análisis en streaming con stream-json

bash
npm install stream-json
Node.js 18+ — stream-json para arrays grandes
import { pipeline } from 'stream/promises'
import { createReadStream } from 'fs'
import { parser } from 'stream-json'
import { streamArray } from 'stream-json/streamers/StreamArray.js'

// events.json = array de millones de objetos — nunca se carga completamente en memoria
await pipeline(
  createReadStream('./events.json'),
  parser(),
  streamArray(),
  async function* (source) {
    for await (const { value: event } of source) {
      if (event.severity === 'error') {
        console.log(JSON.stringify(event, null, 2))
      }
    }
  }
)

NDJSON / JSON Lines — Sin dependencias adicionales

NDJSON (Newline Delimited JSON) almacena un objeto JSON por línea y es común en exportaciones de Kafka, salidas de BigQuery y pipelines de registros estructurados. El módulo integrado readline de Node.js lo gestiona sin paquetes de terceros.

Node.js 18+ — NDJSON con readline
import { createReadStream } from 'fs'
import { createInterface } from 'readline'

// Formato: un objeto JSON por línea (logs, exportaciones de Kafka, BigQuery)
const rl = createInterface({
  input: createReadStream('./logs.ndjson'),
  crlfDelay: Infinity
})

for await (const line of rl) {
  if (!line.trim()) continue
  const entry = JSON.parse(line)
  if (entry.level === 'error') {
    console.log(JSON.stringify(entry, null, 2))
  }
}
Nota:Cambia de JSON.parse() al streaming cuando tu archivo JSON supere los 50–100 MB o cuando proceses un flujo ilimitado (Kafka, pipeline de logs). Para NDJSON / JSON Lines, usa readline — no requiere dependencias adicionales.

Errores comunes

Estos cuatro errores aparecen repetidamente en revisiones de código e informes de bugs en producción. Cada uno implica un comportamiento sutil de JSON.stringify() que es fácil pasar por alto y difícil de depurar después del hecho.

Olvidar que los valores undefined se omiten silenciosamente

Problema: Las propiedades de objeto con valores undefined se omiten completamente de la salida JSON — sin advertencia ni error. Esto provoca una pérdida invisible de datos cuando existen campos opcionales en el objeto.

Solución: Usa null para los valores intencionalmente ausentes que deben aparecer en la salida serializada. Reserva undefined solo para los campos que deben excluirse del JSON.

Before · JavaScript
After · JavaScript
const userProfile = {
  userId: "usr_4421",
  displayName: "Carlos Mendoza",
  avatarUrl: undefined,   // desaparecerá silenciosamente
  bio: undefined          // desaparecerá silenciosamente
}

JSON.stringify(userProfile, null, 2)
// { "userId": "usr_4421", "displayName": "Carlos Mendoza" }
// avatarUrl y bio han desaparecido — sin advertencia
const userProfile = {
  userId: "usr_4421",
  displayName: "Carlos Mendoza",
  avatarUrl: null,   // ausente de forma explícita — aparece en la salida
  bio: null          // ausente de forma explícita — aparece en la salida
}

JSON.stringify(userProfile, null, 2)
// {
//   "userId": "usr_4421",
//   "displayName": "Carlos Mendoza",
//   "avatarUrl": null,
//   "bio": null
// }
BigInt lanza TypeError

Problema: Pasar un valor BigInt a JSON.stringify() lanza un TypeError en tiempo de ejecución. Es un fallo total, no una omisión silenciosa — aparecerá en producción si algún campo numérico supera Number.MAX_SAFE_INTEGER.

Solución: Usa una función replacer que convierta los valores BigInt a cadenas antes de la serialización. Alternativamente, convierte BigInt a string o Number en la capa de datos antes de pasarlo a JSON.stringify.

Before · JavaScript
After · JavaScript
const session = {
  sessionId: 9007199254741234n,  // literal BigInt
  userId: "usr_4421",
  startedAt: "2026-03-10T14:00:00Z"
}

JSON.stringify(session, null, 2)
// Uncaught TypeError: Do not know how to serialize a BigInt
function bigIntReplacer(_key, value) {
  return typeof value === 'bigint' ? value.toString() : value
}

const session = {
  sessionId: 9007199254741234n,
  userId: "usr_4421",
  startedAt: "2026-03-10T14:00:00Z"
}

JSON.stringify(session, bigIntReplacer, 2)
// {
//   "sessionId": "9007199254741234",
//   "userId": "usr_4421",
//   "startedAt": "2026-03-10T14:00:00Z"
// }
Las referencias circulares causan un fallo

Problema: Los objetos que se referencian a sí mismos — habituales en árboles DOM, listas enlazadas y algunos conjuntos de resultados de ORM — lanzan un TypeError al pasarlos a JSON.stringify(). El error solo ocurre en el momento de la serialización, a menudo lejos de donde se creó la referencia circular.

Solución: Usa una función replacer con un WeakSet para detectar y reemplazar las referencias circulares, o instala la librería flatted como reemplazo directo que gestiona estructuras circulares de forma nativa.

Before · JavaScript
After · JavaScript
const dept = { id: "dept_eng", name: "Ingeniería" }
const team = { id: "team_frontend", dept }
dept.teams = [team]  // circular: dept → team → dept

JSON.stringify(dept, null, 2)
// Uncaught TypeError: Converting circular structure to JSON
import { stringify } from 'flatted'  // npm install flatted

const dept = { id: "dept_eng", name: "Ingeniería" }
const team = { id: "team_frontend", dept }
dept.teams = [team]

// flatted gestiona refs circulares — nota: la salida es formato flatted, no JSON estándar
stringify(dept)

// O usa un replacer basado en WeakSet si necesitas JSON estándar:
function circularReplacer() {
  const seen = new WeakSet()
  return (_key, value) => {
    if (typeof value === 'object' && value !== null) {
      if (seen.has(value)) return '[Circular]'
      seen.add(value)
    }
    return value
  }
}
JSON.stringify(dept, circularReplacer(), 2)
Usar space > 10 (limitado silenciosamente)

Problema: Pasar un valor de space mayor que 10 a JSON.stringify() no lanza un error — el valor se limita silenciosamente a 10. Los desarrolladores que esperan 20 espacios por nivel de sangría para estructuras profundas obtendrán solo 10, lo que lleva a un formato inesperado en los archivos generados.

Solución: La sangría máxima es de 10 espacios. Para estructuras profundas, prefiere una sangría de 2 espacios (la convención más común en proyectos JavaScript) y confía en editores con colapso para la navegación.

Before · JavaScript
After · JavaScript
const deepConfig = { server: { tls: { certs: { primary: "/etc/ssl/api.pem" } } } }

// Se espera una sangría de 20 espacios — pero space se limita a 10
JSON.stringify(deepConfig, null, 20)
// Misma salida que JSON.stringify(deepConfig, null, 10)
// Sin error, sin advertencia — truncado silenciosamente
const deepConfig = { server: { tls: { certs: { primary: "/etc/ssl/api.pem" } } } }

// Usa 2 (la mayoría de proyectos) o 4 espacios — nunca superes 10
JSON.stringify(deepConfig, null, 2)
// {
//   "server": {
//     "tls": {
//       "certs": {
//         "primary": "/etc/ssl/api.pem"
//       }
//     }
//   }
// }

JSON.stringify vs alternativas — Comparación rápida

Diferentes situaciones requieren diferentes herramientas. JSON.stringify con un replacer cubre la mayoría de casos de uso en producción sin dependencias. util.inspect es la elección correcta para la depuración rápida en terminal cuando necesitas salida con colores y no necesitas JSON válido. fast-json-stringify vale la pena en rutas de alto rendimiento donde el perfil muestra que la serialización tiene coste; para todo lo demás, la sobrecarga del mantenimiento del esquema no merece la pena.

Método
Salida formateada
JSON válido
No-ASCII
Tipos personalizados
Refs. circulares
Requiere instalación
JSON.stringify
⚠️ vía replacer
✗ (lanza error)
No
JSON.stringify + replacer
✅ control total
No
util.inspect
✅ nativo
No (integrado en Node)
fast-json-stringify
❌ solo esquema
npm install
flatted
✗ (formato propio)
✅ solo circulares
npm install
jq (CLI)
N/A
N/A
Instalación del sistema

Preguntas frecuentes

¿Cómo formateo JSON con sangría en JavaScript?

Llama a JSON.stringify(data, null, 2): el tercer argumento controla la sangría. Pasa 2 o 4 para espacios, o "\t" para tabulaciones. No se necesita ninguna importación ni instalación: JSON es un objeto global en todos los entornos JavaScript, incluidos navegadores y Node.js.

JavaScript
const config = { host: "api.payments.internal", port: 8443, tls: true }
console.log(JSON.stringify(config, null, 2))
// {
//   "host": "api.payments.internal",
//   "port": 8443,
//   "tls": true
// }

¿Qué hace el parámetro `space` en JSON.stringify()?

El parámetro space controla la sangría de la salida. Pasa un número (1–10) para tantos espacios por nivel, o una cadena como "\t" para usar un carácter de tabulación. Los valores superiores a 10 se limitan silenciosamente a 10. Pasar null, 0 u omitir el parámetro produce un JSON compacto en una sola línea.

JavaScript
const data = { service: "payments", version: 3, active: true }

JSON.stringify(data, null, 2)   // sangría de 2 espacios
JSON.stringify(data, null, 4)   // sangría de 4 espacios
JSON.stringify(data, null, '\t') // sangría con tabulación
JSON.stringify(data)            // compacto: {"service":"payments","version":3,"active":true}

¿Por qué JSON.stringify() devuelve undefined para algunos valores?

JSON.stringify omite silenciosamente las propiedades de objetos cuyos valores son undefined, funciones o Symbols, ya que estos tipos no tienen representación en JSON. Si el valor de nivel superior es undefined, la función devuelve undefined (no la cadena "undefined"). Usa null en lugar de undefined para los campos opcionales que deben aparecer en la salida.

JavaScript
const event = {
  traceId: "tr_9a2f",
  handler: () => {},        // función — se omite
  requestId: undefined,     // undefined — se omite
  sessionId: Symbol("s"),   // Symbol — se omite
  status: "ok"
}
JSON.stringify(event, null, 2)
// { "traceId": "tr_9a2f", "status": "ok" }

¿Cómo manejo objetos Date al formatear JSON?

Los objetos Date tienen un método toJSON() integrado que devuelve una cadena ISO 8601, por lo que JSON.stringify los gestiona automáticamente. No necesitas un replacer personalizado para fechas: el valor serializado será una cadena como "2026-03-10T14:22:00.000Z".

JavaScript
const order = {
  orderId: "ord_8f2a91bc",
  placedAt: new Date("2026-03-10T14:22:00Z"),
  total: 42.98
}
JSON.stringify(order, null, 2)
// {
//   "orderId": "ord_8f2a91bc",
//   "placedAt": "2026-03-10T14:22:00.000Z",
//   "total": 42.98
// }

¿Cómo formateo una cadena JSON (no un objeto) en JavaScript?

Primero analiza la cadena con JSON.parse() y luego vuelve a serializarla con JSON.stringify(). Ambas llamadas se pueden encadenar en una sola línea para depuración rápida.

JavaScript
const raw = '{"endpoint":"/api/v2/users","timeout":30,"retry":true}'
const formatted = JSON.stringify(JSON.parse(raw), null, 2)
console.log(formatted)
// {
//   "endpoint": "/api/v2/users",
//   "timeout": 30,
//   "retry": true
// }

¿Puedo usar JSON.stringify() en el navegador?

Sí. JSON es un objeto global integrado en todos los navegadores modernos desde IE8: no se necesitan etiquetas de script ni importaciones. Abre la consola de DevTools y llama a JSON.stringify() directamente. Funciona de forma idéntica a la versión de Node.js, con la misma firma de parámetros y las mismas limitaciones respecto a BigInt y referencias circulares.

JavaScript
// Funciona en Chrome, Firefox, Safari, Edge — sin importaciones
const payload = { userId: "usr_7b3c", action: "checkout", cart: ["SKU-001", "SKU-002"] }
copy(JSON.stringify(payload, null, 2)) // copy() es un helper de DevTools

JavaScript te da control total — funciones replacer, toJSON() personalizado, procesamiento de archivos grandes en Node.js. Cuando solo necesitas inspeccionar o compartir un fragmento formateado, el Formateador JSON de ToolDeck es el camino más rápido: pega tu JSON y obtén un resultado formateado y resaltado sin configurar ningún entorno.

Herramientas relacionadas

También disponible en:PythonGoBash
AC
Alex ChenFront-end & Node.js Developer

Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.

MW
Marcus WebbRevisor técnico

Marcus specialises in JavaScript performance, build tooling, and the inner workings of the V8 engine. He has spent years profiling and optimising React applications, working on bundler configurations, and squeezing every millisecond out of critical rendering paths. He writes about Core Web Vitals, JavaScript memory management, and the tools developers reach for when performance really matters.