Artigo Build·Desenvolvimento·13 min de leitura de leitura

Como Converter JSON para CSV em JavaScript (2026): Guia Completo com Código

Converter JSON para CSV parece trivial até você topar com objetos aninhados, acentuação, vírgulas dentro de campos e encoding. Este guia mostra abordagens manuais e com PapaParse, tratamento de edge cases, geração de download no browser e ingestão em banco de dados — com código pronto para copiar.

Vitor Morais

Por Vitor Morais

Fundador do MochaLabz ·

↔️

Converta JSON ↔ CSV online

Cole JSON, baixe CSV (ou inverso) — tudo no navegador, sem enviar dados pra servidor.

Usar conversor →

Converter JSON em CSV é uma das tarefas mais recorrentes no dia a dia de um dev: API devolve JSON, cliente pede planilha Excel; banco exporta dados em JSON, analista quer abrir no Google Sheets; dashboard coleta eventos em JSON, compliance quer CSV para auditoria. Parece trivial — até você topar com objetos aninhados, vírgulas em campos de texto, acentuação em UTF-8 que quebra no Excel e linhas com schema divergente.

Este guia cobre o caminho completo: abordagem manual (para entender o problema), uso de PapaParse (padrão da indústria), tratamento de edge cases, download no browser, performance em arquivos grandes e ingestão em banco a partir do CSV gerado.

O problema em uma frase

JSON é hierárquico e tipado (strings, números, booleanos, arrays, objetos aninhados). CSV é plano (linhas × colunas) e só tem texto. A conversão exige decidir como representar hierarquia em duas dimensões e como preservar tipos que CSV não conhece.

Conversão manual: o ponto de partida

Para entender o que está acontecendo, implemente sem biblioteca:

function jsonToCsv(jsonArray: Record<string, unknown>[]): string { if (jsonArray.length === 0) return ""; // Usa as chaves do primeiro objeto como colunas const fields = Object.keys(jsonArray[0]); // Cabeçalho const header = fields.map(escape).join(","); // Linhas const rows = jsonArray.map((row) => fields.map((f) => escape(String(row[f] ?? ""))).join(","), ); return [header, ...rows].join("\n"); } function escape(value: string): string { // Escapa aspas duplas e envolve em aspas se contiver , " ou \n if (/[,"\n]/.test(value)) { return `"${value.replace(/"/g, '""')}"`; } return value; } // Uso const data = [ { id: 1, nome: "João", email: "joao@exemplo.com" }, { id: 2, nome: 'Maria "Silva"', email: "maria@exemplo.com" }, ]; console.log(jsonToCsv(data)); // id,nome,email // 1,João,joao@exemplo.com // 2,"Maria ""Silva""",maria@exemplo.com

Atenção

Código manual como o acima cobre o básico mas falha em edge cases: arrays aninhados, objetos dentro de objetos, valores null, booleanos, datas serializadas de formas diferentes. Para produção, use PapaParse.

PapaParse: padrão da indústria

npm install papaparse npm install -D @types/papaparse

Uso básico

import Papa from "papaparse"; const data = [ { id: 1, nome: "João", email: "joao@exemplo.com" }, { id: 2, nome: 'Maria "Silva"', email: "maria@exemplo.com" }, ]; const csv = Papa.unparse(data); console.log(csv); // id,nome,email // 1,João,joao@exemplo.com // 2,"Maria ""Silva""",maria@exemplo.com

Com configuração avançada

const csv = Papa.unparse(data, { delimiter: ",", // padrão; pode usar ";" para Excel PT-BR newline: "\n", // ou "\r\n" para Windows quotes: false, // true força aspas em todos os campos quoteChar: '"', escapeChar: '"', header: true, // inclui cabeçalho (padrão true) columns: ["id", "nome", "email"], // ordem e campos fixos });

Dica

Excel em português usa ponto-e-vírgula (;) como separador por padrão (o ponto decimal é vírgula). Se gerar CSV para usuários brasileiros abrindo no Excel local, use delimiter: “;”. Para ferramentas internacionais (Google Sheets, Airtable), vírgula é o padrão.

Flatten: lidando com objetos aninhados

CSV não suporta hierarquia. Se você tem JSON como:

[ { id: 1, nome: "João", endereco: { rua: "Av. Paulista", cidade: "São Paulo", uf: "SP" } } ]

Você precisa transformar em algo como:

id,nome,endereco.rua,endereco.cidade,endereco.uf 1,João,Av. Paulista,São Paulo,SP

Função de flatten

function flatten( obj: Record<string, unknown>, prefix = "", ): Record<string, unknown> { const result: Record<string, unknown> = {}; for (const key of Object.keys(obj)) { const value = obj[key]; const newKey = prefix ? `${prefix}.${key}` : key; if ( value !== null && typeof value === "object" && !Array.isArray(value) ) { Object.assign(result, flatten(value as Record<string, unknown>, newKey)); } else if (Array.isArray(value)) { result[newKey] = JSON.stringify(value); // arrays viram JSON string } else { result[newKey] = value; } } return result; } // Uso const flatData = data.map((row) => flatten(row)); const csv = Papa.unparse(flatData);

Encoding: BOM para Excel abrir corretamente

O maior problema prático é Excel abrindo CSV com acento corrompido. Causa: Excel em Windows tenta detectar encoding a partir de BOM. Sem BOM, assume Windows-1252 (não-UTF-8), e acentos quebram.

function jsonToCsvComBom(data: Record<string, unknown>[]): string { const csv = Papa.unparse(data); return "\uFEFF" + csv; // BOM no início } // Ao gerar Blob no browser const csvComBom = jsonToCsvComBom(data); const blob = new Blob([csvComBom], { type: "text/csv;charset=utf-8", });

Contexto

Google Sheets, Airtable e maioria de leitores modernos funcionam com ou sem BOM. Excel é o caso especial. Como adicionar BOM não quebra nada, acostume-se a sempre adicionar — economiza suporte e dor de cabeça.

Download no browser: o padrão

function baixarCsv( data: Record<string, unknown>[], nome = "dados.csv", ) { const csv = "\uFEFF" + Papa.unparse(data); const blob = new Blob([csv], { type: "text/csv;charset=utf-8" }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = nome; link.click(); URL.revokeObjectURL(url); // libera memória } // Uso baixarCsv(meusdados, "relatorio-abril.csv");

Node.js: escrevendo direto no arquivo

import Papa from "papaparse"; import { writeFileSync } from "node:fs"; const data = [ { id: 1, nome: "João" }, { id: 2, nome: "Maria" }, ]; const csv = "\uFEFF" + Papa.unparse(data); writeFileSync("./dados.csv", csv, "utf8"); console.log("CSV exportado com sucesso.");

Para arquivos grandes: streaming

import { createWriteStream } from "node:fs"; import Papa from "papaparse"; const stream = createWriteStream("./dados-grandes.csv"); // Escreve BOM stream.write("\uFEFF"); // Escreve header stream.write(Papa.unparse([{}], { header: true, columns: ["id", "nome"] })); stream.write("\n"); // Escreve linhas em batch for (const batch of getBatchesFromDb()) { const chunk = Papa.unparse(batch, { header: false }); stream.write(chunk); stream.write("\n"); } stream.end();

Edge cases comuns

Como CSV lida com tipos JSON
CritérioRepresentação em CSV
String simplesJoão
String com vírgula"João, Maria"
String com aspas"João ""Silva"""
String com nova linha"Texto\nquebrado"
Número inteiro42
Número decimal3.14
Booleantrue | false
null(célula vazia)
undefined(célula vazia)
Array de strings"[\"a\",\"b\"]"
Objeto aninhadoflatten ou JSON stringify
Data ISO 86012026-04-20T12:00:00Z

Preservando tipos com Excel: exportando .xlsx

CSV perde tipos — tudo vira string. Para preservar data, número decimal e formatação, use SheetJS para gerar .xlsx direto:

npm install xlsx
import * as XLSX from "xlsx"; function baixarXlsx(data: Record<string, unknown>[], nome = "dados.xlsx") { const worksheet = XLSX.utils.json_to_sheet(data); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, "Dados"); XLSX.writeFile(workbook, nome); } baixarXlsx(meusdados, "relatorio.xlsx");

Benchmark: CSV vs XLSX vs JSON

Comparação de formatos para exportação de dados
CritérioVantagemDesvantagem
CSVUniversal, rápido, levePerde tipos, sensível a delimitador
XLSX (Excel)Preserva tipos, formataçãoBinário, mais pesado
JSONPreserva tipos, hierarquiaNão abre no Excel, analista sofre
ParquetCompressão, tipos, eficiente para big dataInútil pra usuário final
TSVSimilar ao CSV, menos conflito de vírgulasMenos suportado

Escolhendo a ferramenta certa

  • Export pequeno para o usuário final: CSV com BOM no frontend, download automático.
  • Relatório técnico para analista: XLSX via SheetJS (tipos preservados).
  • Integração entre sistemas: JSON.
  • Big data (milhões+ de linhas): Parquet ou compression CSV (.csv.gz).
  • Ingestão em banco a partir de CSV: ver artigo importar CSV para banco.

Componente React pronto

"use client"; import Papa from "papaparse"; interface BotaoExportarProps { data: Record<string, unknown>[]; nomeArquivo?: string; children?: React.ReactNode; } export function BotaoExportarCsv({ data, nomeArquivo = "dados.csv", children = "Exportar CSV", }: BotaoExportarProps) { const handleClick = () => { const csv = "\uFEFF" + Papa.unparse(data); const blob = new Blob([csv], { type: "text/csv;charset=utf-8" }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = nomeArquivo; link.click(); URL.revokeObjectURL(url); }; return ( <button onClick={handleClick} disabled={data.length === 0} className="px-4 py-2 bg-caramel-500 text-white rounded disabled:opacity-50" > {children} </button> ); } // Uso <BotaoExportarCsv data={pedidos} nomeArquivo="pedidos-abril.csv" />

Performance em arquivos grandes

PapaParse é rápido, mas há limites. Referência em hardware moderno:

Performance aproximada de Papa.unparse
CritérioTempo
1.000 linhas × 10 colunas~20 ms
10.000 linhas × 10 colunas~150 ms
100.000 linhas × 10 colunas~1,5 s
1.000.000 linhas × 10 colunas~15-25 s (pode congelar UI)

Vai mais fundo

Para arquivos que passam de 100k linhas no browser, considere: (1) Web Worker — conversão em thread separada sem congelar UI; (2) streaming direto do servidor com geração progressiva; (3) arquivo comprimido (gzip) para acelerar download.

Erros clássicos

  • Esquecer BOM para Excel: usuário abre arquivo com acentuação quebrada.
  • Delimitador errado em PT-BR: vírgula em vez de ponto-e-vírgula abre tudo em uma coluna no Excel brasileiro.
  • Não escapar aspas internas: arquivo fica malformado.
  • Ordem de colunas inconsistente: cada export tem ordem diferente — automação quebra.
  • Vazar memória de Blob URL: não chamar revokeObjectURL em loop.
  • Serializar Date como número: new Date() em CSV vira número. Use toISOString() ou pedir ao PapaParse para serializar.
  • Rodar conversão no main thread com 1M linhas: congela a UI por segundos.
  • Não validar campos antes de exportar: campo undefined gera célula vazia inexplicada.

Fluxo completo end-to-end

Um exemplo realista: API devolve JSON paginado, frontend consolida e oferece botão para exportar:

"use client"; import { useState } from "react"; import Papa from "papaparse"; async function buscarTodosDados() { const todosRegistros: Record<string, unknown>[] = []; let pagina = 1; let temMais = true; while (temMais) { const res = await fetch(`/api/pedidos?page=${pagina}`); const { data, hasMore } = await res.json(); todosRegistros.push(...data); temMais = hasMore; pagina++; } return todosRegistros; } export function ExportarPedidos() { const [loading, setLoading] = useState(false); const handleExport = async () => { setLoading(true); try { const dados = await buscarTodosDados(); const csv = "\uFEFF" + Papa.unparse(dados, { columns: ["id", "cliente", "valor", "criado_em", "status"], }); const blob = new Blob([csv], { type: "text/csv;charset=utf-8" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `pedidos-${new Date().toISOString().slice(0, 10)}.csv`; a.click(); URL.revokeObjectURL(url); } finally { setLoading(false); } }; return ( <button onClick={handleExport} disabled={loading}> {loading ? "Exportando..." : "Exportar Pedidos em CSV"} </button> ); }

Conversão em uma frase

JSON para CSV é uma daquelas tarefas enganosamente simples. Com PapaParse, BOM e cuidado em edge cases (vírgulas, aspas, objetos aninhados, encoding), o código resultante é robusto em produção e suporta milhões de linhas sem surpresa. Sem biblioteca, você reimplementa RFC 4180 e vira mantenedor involuntário de casos-limite — caminho quase nunca justificável.

Perguntas frequentes

Qual a biblioteca JavaScript mais usada para converter JSON em CSV?+

PapaParse é a referência absoluta em 2026. Suporta conversão de/para CSV com lidar com edge cases (aspas, vírgulas em campos, escape), tem API clara e roda em browser e Node. Alternativas: json2csv (mais focada em Node), csv-writer (simples), ou conversão manual para projetos mínimos onde peso do bundle importa. Para 90% dos casos, PapaParse.

Como lidar com objetos JSON aninhados ao converter para CSV?+

CSV é bidimensional (linhas × colunas); JSON pode ser hierárquico. Três estratégias: (1) flatten — transforma {a: {b: 1}} em {a.b: 1}; (2) stringify — converte objeto aninhado em string JSON dentro da célula; (3) descarte — ignora campos aninhados. PapaParse não faz flatten automaticamente — use flat ou flatten-js antes, ou escreva função recursiva de flattening.

Preciso adicionar BOM no início do CSV para Excel?+

Sim, se você abre CSV direto no Excel e precisa que acentos apareçam corretos. BOM (Byte Order Mark — \uFEFF) no início do arquivo sinaliza ao Excel que o encoding é UTF-8. Sem ele, caracteres acentuados aparecem corrompidos. Em exportação programática, sempre adicione BOM: const csvWithBom = '\uFEFF' + csv.

Como lidar com vírgulas dentro de campos de texto?+

CSV usa vírgula como separador. Se um campo contém vírgula, ele precisa ser envolvido em aspas duplas: &ldquo;Campo, com vírgula&rdquo;. Se contém aspas duplas, essas precisam ser escapadas duplicando-as: &ldquo;Campo com \&quot;aspas\&quot; internas&rdquo;. PapaParse faz isso automaticamente. Implementações manuais precisam cobrir os dois casos — falha comum em código caseiro.

É mais rápido converter JSON para CSV no backend ou no frontend?+

Para arquivos pequenos (até 10 MB ou 100k linhas), o browser moderno converte em milissegundos. Vantagens: zero carga no servidor, download instantâneo. Para arquivos grandes (centenas de MB ou milhões de linhas), prefira backend — o browser pode congelar durante conversão sync. Use streaming de escrita de arquivo em Node.js. Em dúvida, teste: sua estimativa de &ldquo;grande&rdquo; pode ser menor do que imagina.

Como gerar download automático do CSV no browser?+

Três linhas: criar Blob com conteúdo CSV, criar URL via URL.createObjectURL, criar elemento &lt;a&gt; com download attribute, chamar click(). No código: const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'dados.csv'; a.click(); URL.revokeObjectURL(url). Nunca esqueça revokeObjectURL para não vazar memória.

CSV é o melhor formato para exportar dados tabulares?+

Para interoperabilidade com Excel e Google Sheets, sim — CSV é o padrão universal. Para dados que serão consumidos por outro programa (API, pipeline), JSON ou Parquet são melhores. Para conjuntos grandes com tipos preservados (datas, números com decimais), Excel (.xlsx) via biblioteca SheetJS é superior a CSV puro. Escolha pelo consumidor, não pelo que é mais fácil de gerar.

Como preservar a ordem das colunas no CSV?+

Defina explicitamente as colunas antes da conversão: const fields = ['id', 'nome', 'email']; Papa.unparse({ fields, data: jsonArray }). Sem essa definição, PapaParse usa a ordem de propriedades do primeiro objeto, que é não-determinística em JavaScript para objetos criados dinamicamente. Para exports em produção (relatórios, cobranças), definição explícita é obrigatória.

#json#csv#javascript#typescript#papaparse#xlsx#export#node.js#excel#download

Artigos relacionados