La forma más rápida de formatear JSON en JavaScript es JSON.stringify(value, null, 2). El tercer argumento, el parámetro space, le indica al motor que indentifique cada nivel anidado, convirtiendo una línea densa en una salida legible y bien formateada. Esa línea única cubre la mayoría de los casos, pero falla con referencias circulares, elimina funciones y undefined silenciosamente, y no te da control sobre qué campos incluir. Esta guía recorre el camino completo del código y los bordes afilados que todo tutorial omite.
Si solo necesitas una salida limpia una vez y no quieres escribir un script desechable, puedes pegar el texto sin formato en un formateador JSON online gratuito y copiar el resultado. Para todo lo que debe suceder dentro de tu código, las secciones a continuación cubren los argumentos, los problemas y los patrones que realmente importan en producción.
Cómo formatear JSON en JavaScript con JSON.stringify#
JSON.stringify() recibe tres argumentos: el valor a serializar, un replacer opcional y un space opcional. Para una impresión bonita, omite el replacer con null y pasa un número o cadena como space.
const user = { name: "Ada", roles: ["admin", "editor"], active: true };
// Compacto (por defecto), todo en una línea
JSON.stringify(user);
// {"name":"Ada","roles":["admin","editor"],"active":true}
// Impresión bonita con sangría de 2 espacios
JSON.stringify(user, null, 2);
// {
// "name": "Ada",
// "roles": [
// "admin",
// "editor"
// ],
// "active": true
// }
El argumento space es el que hace el trabajo. Pasa un número del 1 al 10 para esa cantidad de espacios de sangría por nivel, o pasa una cadena (como "\t") para sangrar con esa cadena.
Los números mayores a 10 se limitan a 10.
JSON.stringify(obj, null, 50)se comporta exactamente comoJSON.stringify(obj, null, 10), así que no esperes una sangría más profunda pasando un número mayor.
El parámetro space: números vs cadenas#
El argumento space acepta dos tipos, y el tipo cambia el estilo de salida:
Valor de space | Resultado | Uso común |
|---|---|---|
2 | Dos espacios por nivel | JavaScript, configuración JSON, ecosistemas web |
4 | Cuatro espacios por nivel | Herramientas Python, algunas salidas Java/.NET |
"\t" | Un tabulador por nivel | Repositorios que exigen sangría con tabulador |
0, null u omitido | Sin formato, una sola línea | Cargas útiles de API, almacenamiento, transferencia de red |
Una cadena space permite hacer cosas que un número no puede. Pasa " " (dos espacios) para el mismo efecto que 2, o pasa algo visual como "--" para depurar la profundidad de anidamiento. Solo se usan los primeros 10 caracteres de una cadena space.
Por qué realmente importa 2 vs 4 espacios#
Esto no es una discusión trivial. El ancho de sangría es una convención del ecosistema, y mantenerlo hace que tu salida sea coherente con las herramientas que la leerán después.
- 2 espacios es el estándar de facto en el mundo JavaScript y Node. Prettier usa 2 por defecto, el
package.jsonde npm usa 2, y la mayoría de los archivos de configuración JSON que tocarás (tsconfig.json,.eslintrc.json) siguen esa norma. - 4 espacios aparece cuando el lenguaje circundante usa 4, sobre todo en
json.dumps(obj, indent=4)de Python. Si un archivo JSON está junto a código Python o es generado por un servicio Python, 4 mantiene la alineación visual. - Tabuladores aparecen en repositorios con una guía de estilo basada en tabuladores. La ventaja es que cada desarrollador puede configurar su propio ancho de visualización del tabulador.
Si tu proyecto ejecuta Prettier o un formateador al hacer commit, ajusta su configuración para no luchar contra el autoformateador. En caso de duda, usa 2.
Uso de una función de reemplazo para controlar la salida#
El segundo argumento, replacer, es la característica menos utilizada de JSON.stringify. Controla qué propiedades se serializan y cómo se transforman sus valores. Acepta un array o una función.
Reemplazo como array de lista blanca#
Pasa un array de cadenas con nombres de propiedades y solo esas claves sobreviven. Esta es la forma más limpia de eliminar campos sensibles o ruidosos antes de registrar o enviar datos.
const account = {
id: 42,
email: "[email protected]",
passwordHash: "$2b$10$...",
sessionToken: "abc123",
};
// Solo id y email aparecen en la salida
JSON.stringify(account, ["id", "email"], 2);
// {
// "id": 42,
// "email": "[email protected]"
// }
La lista blanca se aplica en cada nivel del objeto, por lo que una clave llamada id se conserva dondequiera que aparezca en el árbol. Eso es una ventaja para formas uniformes y una trampa para árboles donde el mismo nombre de clave significa cosas diferentes en distintas profundidades.
Reemplazo como función de transformación#
Pasa una función y se ejecuta para cada par clave/valor. Devuelve el valor que deseas serializar, devuelve undefined para eliminar la clave o transforma sobre la marcha. La función recibe la clave y el valor, y this está vinculado al objeto que contiene la clave.
const order = { total: 19.99, secret: "hide-me", createdAt: new Date() };
const json = JSON.stringify(order, (key, value) => {
if (key === "secret") return undefined; // elimina el campo
if (typeof value === "number") return value.toFixed(2); // formatea números
return value;
}, 2);
Observa el orden de llamada: el reemplazo se invoca primero con una clave vacía y el objeto completo, luego para cada propiedad. Si transformas valores a ciegas sin verificar la clave, puedes alterar accidentalmente la raíz.
Una alternativa más limpia para el control por objeto es el método toJSON(). Cualquier objeto con un método toJSON() (como Date, por lo que las fechas se serializan como cadenas ISO) controla su propia forma serializada antes de que el reemplazo la vea.
Cómo solucionar el error de referencia circular (TypeError)#
Este es el error que todos los tutoriales básicos ignoran, y el que realmente te detendrá en seco. Si un objeto se referencia a sí mismo, directa o indirectamente, JSON.stringify lanza:
TypeError: Converting circular structure to JSON
Ocurre constantemente con nodos del DOM, entidades ORM que enlazan a su padre, objetos de eventos y cualquier dato con forma de grafo.
const node = { name: "root" };
node.self = node; // circular
JSON.stringify(node, null, 2); // TypeError: Converting circular structure to JSON
La solución es un reemplazador que rastree los objetos ya vistos y omita repeticiones:
function safeStringify(value, space = 2) {
const seen = new WeakSet();
return JSON.stringify(value, (key, val) => {
if (typeof val === "object" && val !== null) {
if (seen.has(val)) return "[Circular]";
seen.add(val);
}
return val;
}, space);
}
safeStringify(node);
// {
// "name": "root",
// "self": "[Circular]"
// }
Un WeakSet es la herramienta adecuada aquí porque no impide la recolección de basura de los objetos que rastrea. Si usas un Node reciente o un navegador moderno, el structuredClone global también lanzará un error con datos circulares, por lo que un reemplazador con conjunto de vistos sigue siendo la solución práctica para la serialización.
Valores que desaparecen silenciosamente#
JSON.stringify no solo lanza errores con datos circulares. También elimina o cambia silenciosamente varios tipos de valor, lo que provoca errores confusos donde un campo "desaparece".
- Funciones y
undefinedse omiten por completo de los objetos, y se convierten ennulldentro de los arrays. NaNeInfinityse serializan comonull.BigIntlanza unTypeError. Debes convertirlo a cadena o número primero.Symbolcomo claves y valores se ignoran.Datese convierte en una cadena ISO (a través de sutoJSON), por lo que no vuelve a ser unDateal analizarlo.
Si falta una propiedad en tu salida, casi siempre es por una de estas razones.
Cómo minificar JSON en JavaScript#
Minificar es lo opuesto a formatear con sangría: eliminar todos los espacios en blanco para obtener la carga útil más pequeña posible. Se minifica llamando a JSON.stringify sin el argumento space (o con 0).
const data = { name: "Ada", roles: ["admin", "editor"] };
JSON.stringify(data);
// {"name":"Ada","roles":["admin","editor"]}
Para pasar de formato con sangría a minificado, analice la cadena formateada en un objeto y conviértala nuevamente a cadena sin space:
const pretty = `{
"name": "Ada",
"active": true
}`;
const minified = JSON.stringify(JSON.parse(pretty));
// {"name":"Ada","active":true}
Ese patrón de JSON.parse seguido de JSON.stringify también es la forma más simple de reformatear JSON no confiable o mal indentado: parse normaliza y stringify vuelve a emitir con el espaciado que elijas. El inconveniente es que JSON.parse es estricto, por lo que las comas finales, comillas simples, comentarios o una respuesta HTML accidental lanzarán un SyntaxError antes de que puedas formatear algo.
Cuándo saltarse el código por completo#
Escribir JSON.stringify es lo correcto cuando el formateo ocurre dentro de tu aplicación: registrar, construir archivos de configuración en tiempo de ejecución, normalizar una respuesta de API. Pero muchas veces solo tienes un bloque de JSON feo en el portapapeles y necesitas leerlo de inmediato. Abrir un REPL, analizar, serializar y copiar de vuelta es más lento de lo que debería.
Para eso, un formateador basado en navegador es más rápido y seguro:
- Valida mientras formatea, por lo que un JSON mal formado recibe un error claro con línea y columna en lugar de un lanzamiento críptico.
- Cambias entre sangría de 2 espacios, 4 espacios, tabulador o salida minimizada sin reescribir código.
- Maneja cargas grandes que saturarían tu consola.
- Los buenos se ejecutan completamente del lado del cliente, por lo que tus datos nunca salen del navegador.
El formateador y validador JSON de Molixa hace exactamente esto: pegar, obtener salida formateada y validada, cambiar la sangría y copiar. Si quieres el caso más profundo para tener uno en tu kit diario, nuestro análisis de por qué un formateador JSON es una herramienta diaria para desarrolladores recorre el flujo de trabajo. Y cuando la salida que estás formateando necesita convertirse en código tipado, la guía de JSON a interfaz TypeScript muestra cómo convertir una carga limpia en interfaces reales.
Cómo crear una función reutilizable para formatear JSON#
Si formateas JSON en varios lugares, envuelve el patrón seguro una vez y reutilízalo en lugar de esparcir JSON.stringify(obj, null, 2) por todo el código. Sigue estos pasos para crear un ayudante que embellezca, minimice, maneje tabulaciones y nunca lance errores.
Paso 1: Configurar un conjunto de objetos vistos para detectar referencias circulares#
Comienza con un WeakSet para rastrear objetos que ya has serializado. Un WeakSet es la opción correcta porque mantiene referencias débiles y no bloqueará la recolección de basura de esos objetos.
function formatJSON(value, { indent = 2, safe = true } = {}) {
const seen = new WeakSet();
// el reemplazador viene después
}
El objeto de opciones con valores predeterminados (indent = 2, safe = true) significa que una llamada simple formatJSON(data) hace lo sensato: salida con 2 espacios y segura contra circulares.
Paso 2: Escribir un reemplazador que maneje los tipos no soportados#
Agrega un reemplazador que devuelva un marcador de posición para objetos repetidos y convierta BigInt a cadena para que no lance error. Es la misma corrección de referencia circular de antes, más la protección de BigInt.
const replacer = safe
? (key, val) => {
if (typeof val === "object" && val !== null) {
if (seen.has(val)) return "[Circular]";
seen.add(val);
}
if (typeof val === "bigint") return val.toString();
return val;
}
: null;
Cuando safe es false, el reemplazador es null, por lo que obtienes el comportamiento simple de JSON.stringify para casos donde sabes que los datos están limpios y quieres máxima velocidad.
Paso 3: Convertir a cadena dentro de un try/catch para que nunca lance error#
Envuelve la llamada a JSON.stringify para que cualquier error de serialización restante devuelva un mensaje legible en lugar de bloquear tu registro o ruta de renderizado.
try {
return JSON.stringify(value, replacer, indent);
} catch (err) {
return `// No se pudo formatear: ${err.message}`;
}
}
Paso 4: Llamarlo con la indentación que necesites#
El mismo ayudante ahora cubre todos los modos de formato a través de la opción indent.
formatJSON(someApiResponse); // 2 espacios, seguro
formatJSON(config, { indent: "\t" }); // indentado con tabulación
formatJSON(payload, { indent: 0 }); // minimizado
Coloca esto en un archivo de utilidades y tendrás embellecimiento, minimización, indentación con tabulaciones y las trampas de referencias circulares y BigInt resueltas en un solo lugar.
Formatear JSON en JavaScript: La versión corta#
Para formatear JSON en JavaScript, usa primero JSON.stringify(value, null, 2). Es la herramienta correcta nueve de cada diez veces. Recuerda las tres cosas que los tutoriales omiten: un array replacer es una lista blanca limpia para eliminar campos sensibles, un replacer con conjunto visto corrige el TypeError de referencias circulares, y varios tipos de valor (funciones, undefined, BigInt, NaN) se eliminan o lanzan un error en lugar de serializarse.
Cuando el JSON está en tu portapapeles en lugar de en tu código, omite el script y pégalo en un formateador y validador de JSON para obtener un formateo instantáneo, validación y minificación con un clic. Ajusta la sangría (generalmente 2 espacios) a tu ecosistema, mantén la función reutilizable a mano y nunca más lucharás con JSON ilegible.
Preguntas Frecuentes#
¿Cómo formateo JSON en JavaScript para que sea legible?
Usa JSON.stringify(valor, null, 2). El tercer argumento es el parámetro space: pasar 2 sangra cada nivel anidado con dos espacios, produciendo una salida legible. Pasa 4 para cuatro espacios o "\t" para sangría con tabulación.
¿Qué hace el tercer argumento de JSON.stringify?
El tercer argumento es space, que controla la sangría. Un número del 1 al 10 sangra esa cantidad de espacios por nivel, y una cadena usa esa cadena como sangría (solo cuentan los primeros 10 caracteres). Omitirlo, o pasar 0 o null, produce una salida compacta de una sola línea sin formato.
¿Por qué JSON.stringify lanza "Converting circular structure to JSON"?
Porque un objeto se referencia a sí mismo directa o indirectamente, y JSON no tiene forma de representar ese ciclo. Soluciónalo con una función de reemplazo que rastree objetos ya vistos en un WeakSet y devuelva un marcador como "[Circular]" para repeticiones, en lugar de dejar que el motor recurra infinitamente.
¿Cómo minimizo JSON en JavaScript?
Llama a JSON.stringify(valor) sin argumento space, o pasa 0. Para minimizar una cadena formateada existente, ejecuta JSON.stringify(JSON.parse(cadenaFormateada)), que la parsea de vuelta a un objeto y la reemite sin espacios.
¿Por qué desaparecen algunas propiedades al convertir un objeto a cadena?
JSON.stringify elimina silenciosamente funciones, valores undefined y claves Symbol de los objetos, y convierte NaN e Infinity a null. Los valores BigInt lanzan un TypeError directamente. Si falta un campo en tu salida, casi siempre es uno de estos tipos no soportados.
¿Debería usar 2 o 4 espacios para formatear JSON?
Adapta tu ecosistema. JavaScript y Node usan por defecto 2 espacios (Prettier, npm, tsconfig.json), mientras que las herramientas de Python suelen usar 4. Si tu proyecto ejecuta un formateador al hacer commit, usa el que imponga para no pelear con el autoformateador. En caso de duda, 2 espacios es el valor predeterminado más seguro.



