Como Exportar Dados JSON para Excel (2026): Guia Completo com SheetJS
Exportar JSON para Excel (.xlsx) é tarefa comum em dashboards e relatórios — e enganosamente simples quando se escolhe a biblioteca certa. Este guia cobre SheetJS e ExcelJS em JavaScript, OpenPyXL em Python, múltiplas planilhas, formatação, fórmulas e estratégias para arquivos grandes.
Por Vitor Morais
Fundador do MochaLabz ·
Converta JSON ↔ CSV
Alternativa rápida pra dados simples — abre direto em Excel e Google Sheets.
Usar conversor →Exportar JSON para Excel (.xlsx) é requisito recorrente em qualquer dashboard ou ERP sério. Analista quer receber relatório que abre no Excel. Gestor pede dashboard exportável. Compliance exige evidência em formato preservável. E CSV, embora simples, perde formatação, tipos e capacidade de múltiplas planilhas — limitações que afastam usuários não-técnicos.
Este guia cobre as ferramentas padrão (SheetJS em JavaScript, OpenPyXL em Python), múltiplas planilhas em um único arquivo, preservação de tipos, formatação rica com ExcelJS, geração de fórmulas e estratégias para arquivos grandes que não travam o browser.
Quando Excel vence CSV
| Critério | Excel (.xlsx) | CSV |
|---|---|---|
| Tamanho do arquivo | Maior (comprimido) | Menor |
| Múltiplas planilhas | Sim | Não |
| Preservação de tipos | Sim (datas, números, booleanos) | Não (tudo é string) |
| Formatação visual | Cores, bordas, fonte, gráficos | Não |
| Fórmulas | Sim | Não |
| Performance (1M linhas) | Lento mas funciona | Rápido |
| Integração com outros sistemas | Média | Alta (universal) |
| Abertura em Excel/Sheets | Perfeita | Pode quebrar por encoding/delimitador |
SheetJS: a biblioteca padrão em JavaScript
npm install xlsxExportação básica
import * as XLSX from "xlsx";
const data = [
{ id: 1, nome: "João", email: "joao@exemplo.com", valor: 1500.5 },
{ id: 2, nome: "Maria", email: "maria@exemplo.com", valor: 2300.0 },
{ id: 3, nome: "Pedro", email: "pedro@exemplo.com", valor: 890.75 },
];
// 1. Converte JSON em worksheet
const worksheet = XLSX.utils.json_to_sheet(data);
// 2. Cria workbook e adiciona a sheet
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Clientes");
// 3. Gera arquivo e baixa (no browser)
XLSX.writeFile(workbook, "dados.xlsx");Configurando cabeçalhos personalizados
// Com header customizado
const worksheet = XLSX.utils.json_to_sheet(data, {
header: ["id", "nome", "email", "valor"], // ordem explícita
});
// Ou com nomes em português
const dataFormatada = data.map((row) => ({
ID: row.id,
"Nome Completo": row.nome,
"E-mail": row.email,
"Valor (R$)": row.valor,
}));
const worksheetBr = XLSX.utils.json_to_sheet(dataFormatada);Múltiplas planilhas no mesmo arquivo
const clientes = [...]; // array de clientes
const pedidos = [...]; // array de pedidos
const produtos = [...]; // array de produtos
const workbook = XLSX.utils.book_new();
// Planilha 1: Clientes
const sheetClientes = XLSX.utils.json_to_sheet(clientes);
XLSX.utils.book_append_sheet(workbook, sheetClientes, "Clientes");
// Planilha 2: Pedidos
const sheetPedidos = XLSX.utils.json_to_sheet(pedidos);
XLSX.utils.book_append_sheet(workbook, sheetPedidos, "Pedidos");
// Planilha 3: Produtos
const sheetProdutos = XLSX.utils.json_to_sheet(produtos);
XLSX.utils.book_append_sheet(workbook, sheetProdutos, "Produtos");
XLSX.writeFile(workbook, "relatorio-completo.xlsx");
// Usuário recebe 1 arquivo com 3 abasDica
Preservando tipos: datas, números e booleanos
const data = [
{
id: 1,
nome: "João",
criadoEm: new Date("2026-04-20"), // tipo Date → preserva
valor: 1500.50, // tipo number → preserva decimal
ativo: true, // tipo boolean → preserva
},
];
const worksheet = XLSX.utils.json_to_sheet(data, {
cellDates: true, // importante para datas
});
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Dados");
XLSX.writeFile(workbook, "com-tipos.xlsx");Atenção
“2026-04-20”), SheetJS trata como texto. Converta para Date antes de exportar: new Date(row.criadoEm). Para datas em Excel formato brasileiro (DD/MM/YYYY), aplique formatação customizada: worksheet["A2"].z = "dd/mm/yyyy".Fórmulas no Excel
const data = [
{ produto: "A", preco: 100, quantidade: 5 },
{ produto: "B", preco: 50, quantidade: 10 },
{ produto: "C", preco: 75, quantidade: 3 },
];
const worksheet = XLSX.utils.json_to_sheet(data);
// Adiciona coluna de Total (fórmula = preço * quantidade)
XLSX.utils.sheet_add_aoa(worksheet, [["Total"]], { origin: "D1" });
// Fórmula em D2, D3, D4
for (let i = 2; i <= data.length + 1; i++) {
worksheet[`D${i}`] = { f: `B${i}*C${i}` };
}
// Soma total em D5
worksheet["D5"] = { f: `SUM(D2:D${data.length + 1})` };
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Produtos");
XLSX.writeFile(workbook, "com-formulas.xlsx");ExcelJS: formatação rica (cores, bordas, fontes)
Para relatórios corporativos com branding visual, SheetJS não basta. Use ExcelJS:
npm install exceljsimport ExcelJS from "exceljs";
async function gerarRelatorio(data: any[]) {
const workbook = new ExcelJS.Workbook();
const sheet = workbook.addWorksheet("Relatório");
// Colunas com tipos e formatação
sheet.columns = [
{ header: "ID", key: "id", width: 10 },
{ header: "Nome", key: "nome", width: 30 },
{ header: "E-mail", key: "email", width: 35 },
{
header: "Valor",
key: "valor",
width: 15,
style: { numFmt: '"R$"#,##0.00' },
},
{ header: "Data", key: "data", width: 15, style: { numFmt: "dd/mm/yyyy" } },
];
// Adiciona linhas
sheet.addRows(data);
// Formata cabeçalho
const headerRow = sheet.getRow(1);
headerRow.font = { bold: true, color: { argb: "FFFFFFFF" } };
headerRow.fill = {
type: "pattern",
pattern: "solid",
fgColor: { argb: "FF28190A" }, // mocha
};
headerRow.alignment = { vertical: "middle", horizontal: "center" };
// Bordas em todas as células
sheet.eachRow((row) => {
row.eachCell((cell) => {
cell.border = {
top: { style: "thin" },
left: { style: "thin" },
bottom: { style: "thin" },
right: { style: "thin" },
};
});
});
// Congela cabeçalho ao scroll
sheet.views = [{ state: "frozen", ySplit: 1 }];
// Salvar
if (typeof window !== "undefined") {
// Browser: download
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "relatorio.xlsx";
link.click();
URL.revokeObjectURL(url);
} else {
// Node.js: filesystem
await workbook.xlsx.writeFile("./relatorio.xlsx");
}
}Python: OpenPyXL
pip install openpyxlfrom openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
data = [
{"id": 1, "nome": "João", "email": "joao@exemplo.com", "valor": 1500.50},
{"id": 2, "nome": "Maria", "email": "maria@exemplo.com", "valor": 2300.00},
]
wb = Workbook()
ws = wb.active
ws.title = "Clientes"
# Cabeçalho
headers = list(data[0].keys())
ws.append(headers)
# Linhas
for row in data:
ws.append(list(row.values()))
# Formatação do cabeçalho
header_font = Font(bold=True, color="FFFFFF")
header_fill = PatternFill("solid", fgColor="28190A")
header_alignment = Alignment(horizontal="center", vertical="center")
for cell in ws[1]:
cell.font = header_font
cell.fill = header_fill
cell.alignment = header_alignment
# Ajustar largura das colunas
ws.column_dimensions["A"].width = 10
ws.column_dimensions["B"].width = 30
ws.column_dimensions["C"].width = 35
ws.column_dimensions["D"].width = 15
# Salvar
wb.save("relatorio.xlsx")Pandas para exports simples
pip install pandas openpyxlimport pandas as pd
data = [
{"id": 1, "nome": "João", "valor": 1500.50},
{"id": 2, "nome": "Maria", "valor": 2300.00},
]
df = pd.DataFrame(data)
# Exportação simples (uma planilha)
df.to_excel("dados.xlsx", index=False)
# Múltiplas planilhas
with pd.ExcelWriter("relatorio.xlsx", engine="openpyxl") as writer:
df_clientes.to_excel(writer, sheet_name="Clientes", index=False)
df_pedidos.to_excel(writer, sheet_name="Pedidos", index=False)
df_produtos.to_excel(writer, sheet_name="Produtos", index=False)Casos de uso reais
Dashboard com export de relatório
"use client";
import * as XLSX from "xlsx";
interface Pedido {
id: number;
cliente: string;
valor: number;
data: Date;
}
export function BotaoExportarPedidos({ pedidos }: { pedidos: Pedido[] }) {
const exportar = () => {
const ws = XLSX.utils.json_to_sheet(pedidos, { cellDates: true });
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Pedidos");
const hoje = new Date().toISOString().slice(0, 10);
XLSX.writeFile(wb, `pedidos-${hoje}.xlsx`);
};
return (
<button onClick={exportar} disabled={pedidos.length === 0}>
Exportar {pedidos.length} pedidos
</button>
);
}Relatório mensal com resumo
async function gerarRelatorioMensal(mes: string) {
const pedidos = await db.pedido.findMany({
where: { mesReferencia: mes },
});
const workbook = XLSX.utils.book_new();
// Aba 1: Pedidos detalhados
const sheetPedidos = XLSX.utils.json_to_sheet(pedidos, { cellDates: true });
XLSX.utils.book_append_sheet(workbook, sheetPedidos, "Detalhado");
// Aba 2: Resumo por cliente
const resumoCliente = agruparPorCliente(pedidos);
const sheetResumo = XLSX.utils.json_to_sheet(resumoCliente);
XLSX.utils.book_append_sheet(workbook, sheetResumo, "Por Cliente");
// Aba 3: Totais
const totais = [{
total_pedidos: pedidos.length,
receita_total: pedidos.reduce((s, p) => s + p.valor, 0),
ticket_medio: pedidos.reduce((s, p) => s + p.valor, 0) / pedidos.length,
}];
const sheetTotais = XLSX.utils.json_to_sheet(totais);
XLSX.utils.book_append_sheet(workbook, sheetTotais, "Totais");
XLSX.writeFile(workbook, `relatorio-${mes}.xlsx`);
}Performance em arquivos grandes
| Critério | Tempo em JS moderno |
|---|---|
| 1.000 linhas × 10 colunas | ~50 ms |
| 10.000 linhas | ~300 ms |
| 100.000 linhas | ~3-5 s (UI bloqueada no browser) |
| 1.000.000 linhas | ~30-60 s (usar Node.js) |
Vai mais fundo
Estratégia: quando Excel, quando CSV, quando JSON
| Critério | Formato recomendado | Motivo |
|---|---|---|
| Relatório para analista B2B | .xlsx com múltiplas abas | Preserva tipos, abre perfeito no Excel |
| Export para integração com sistema | .json | Tipos preservados, universal em código |
| Dump grande para análise | .csv | Leve, manipulável em Pandas/SQL |
| Download para usuário final não-técnico | .xlsx | Abre direto no Excel, sem configuração |
| Dados para Google Sheets | .csv ou .xlsx | Google Sheets aceita ambos |
| Log de eventos | .json | Append-friendly, estruturado |
| Backup de banco | .sql ou dump proprietário | Preserva estrutura e constraints |
Formatação específica: valores monetários e percentuais
// Em ExcelJS
sheet.getColumn("valor").numFmt = '"R$"#,##0.00';
sheet.getColumn("percentual").numFmt = "0.00%";
sheet.getColumn("data").numFmt = "dd/mm/yyyy";
sheet.getColumn("datahora").numFmt = "dd/mm/yyyy hh:mm";
// Em OpenPyXL
from openpyxl.styles import NamedStyle
moeda = NamedStyle(name="moeda", number_format='"R$"#,##0.00')
ws["D2"].style = moedaHeaders em negrito + congelar linha
// ExcelJS
const headerRow = sheet.getRow(1);
headerRow.font = { bold: true, size: 12 };
headerRow.fill = {
type: "pattern",
pattern: "solid",
fgColor: { argb: "FFE0E0E0" },
};
// Congela linha 1 (ficará sempre visível no scroll)
sheet.views = [{ state: "frozen", ySplit: 1 }];Validação de dados na célula (dropdown, regra)
// ExcelJS - dropdown com valores válidos
sheet.getCell("E2").dataValidation = {
type: "list",
allowBlank: false,
formulae: ['"ativo,inativo,pendente"'],
showErrorMessage: true,
errorStyle: "error",
errorTitle: "Status inválido",
error: "Selecione: ativo, inativo ou pendente",
};Proteção de planilha (read-only)
// ExcelJS - protege toda a planilha (só leitura)
await sheet.protect("senha123", {
selectLockedCells: true,
selectUnlockedCells: true,
});
// Permite edição de célula específica
sheet.getCell("E2").protection = { locked: false };Erros clássicos
- Datas viradas número (44000...): não configurou cellDates. Converta para Date antes ou use cellDates: true.
- Valor decimal virar inteiro: JSON enviou como string. Converta explicitamente para number.
- Acento quebrado na abertura: arquivo gerado sem UTF-8. SheetJS gera UTF-8 por padrão — se quebrou, problema no JSON de origem.
- Aba sem nome ou duplicada: Excel não aceita duas abas com mesmo nome. Valide.
- Arquivo grande congelando browser: use Web Worker ou Node.js backend.
- Fórmula não recalculando: Excel precisa abrir o arquivo para calcular. Ao abrir, valores aparecem.
- Formatação não aplicada: aplicou antes de addRows. Aplique depois das linhas existirem.
Fluxo completo end-to-end
Cenário típico: API paginada devolve pedidos; frontend oferece botão para exportar relatório do mês:
"use client";
import { useState } from "react";
import * as XLSX from "xlsx";
async function buscarTodosPedidos(mes: string) {
const todos = [];
let pagina = 1;
while (true) {
const res = await fetch(`/api/pedidos?mes=${mes}&page=${pagina}`);
const { data, hasMore } = await res.json();
todos.push(...data);
if (!hasMore) break;
pagina++;
}
return todos;
}
export function ExportarPedidosBotao({ mes }: { mes: string }) {
const [loading, setLoading] = useState(false);
const handleClick = async () => {
setLoading(true);
try {
const pedidos = await buscarTodosPedidos(mes);
// Converter datas string → Date
const pedidosComDate = pedidos.map((p) => ({
...p,
criadoEm: new Date(p.criadoEm),
}));
const wb = XLSX.utils.book_new();
const sheetDetalhado = XLSX.utils.json_to_sheet(pedidosComDate, {
cellDates: true,
});
XLSX.utils.book_append_sheet(wb, sheetDetalhado, "Pedidos");
// Resumo
const totais = [{
total: pedidos.length,
receita: pedidos.reduce((s, p) => s + p.valor, 0),
ticketMedio: pedidos.reduce((s, p) => s + p.valor, 0) / pedidos.length,
}];
const sheetResumo = XLSX.utils.json_to_sheet(totais);
XLSX.utils.book_append_sheet(wb, sheetResumo, "Resumo");
XLSX.writeFile(wb, `pedidos-${mes}.xlsx`);
} catch (err) {
console.error(err);
alert("Erro ao exportar");
} finally {
setLoading(false);
}
};
return (
<button onClick={handleClick} disabled={loading}>
{loading ? "Gerando..." : `Exportar ${mes} em Excel`}
</button>
);
}Exportar em uma frase
Exportar JSON para Excel em 2026 é tarefa resolvida: SheetJS cobre 90% dos casos em 10 linhas de código, ExcelJS adiciona formatação rica quando necessário, OpenPyXL entrega mesmo para Python. Preservação de tipos, múltiplas abas e fórmulas são suportadas nativamente. A complexidade vem de performance em arquivos grandes (use backend Node.js) e de formatação visual elaborada (use ExcelJS).
Perguntas frequentes
Qual biblioteca usar para exportar JSON para Excel em JavaScript?+
SheetJS (xlsx) é o padrão absoluto em 2026. Suporta leitura e escrita de .xlsx, .xls, .ods, .csv. Gera no browser e Node.js. API limpa via XLSX.utils.json_to_sheet e XLSX.writeFile. Alternativa: exceljs, mais focada em formatação avançada (bordas, cores, gráficos). Para 90% dos casos, SheetJS basta. Para relatórios complexos com branding visual, considere exceljs.
Excel ou CSV — qual escolher?+
Excel (.xlsx) preserva tipos (datas, números, fórmulas), suporta múltiplas planilhas, cores e formatação. CSV é texto simples, universal, mais leve. Use Excel quando: relatório formal, contém dados de tipos variados, será editado pelo usuário final. Use CSV quando: integração com outros sistemas, exportação grande (100k+ linhas), compatibilidade máxima. Para usuário brasileiro final abrindo em Excel, .xlsx vence sempre.
Como exportar múltiplas planilhas em um único arquivo?+
Em SheetJS: cria workbook, adiciona várias sheets via XLSX.utils.book_append_sheet(wb, sheet, 'nome'). Cada sheet é um JSON diferente. Útil para relatórios segmentados (aba Clientes, aba Pedidos, aba Resumo). Em OpenPyXL (Python): workbook.create_sheet('nome') para cada. Usuário recebe um .xlsx único com todas as abas preenchidas. Padrão em exportação de dashboard.
Excel abre arquivo grande (100k+ linhas) sem travar?+
Excel moderno (2019+) abre até 1 milhão de linhas por aba sem travar, embora lento. Para volumes maiores ou visualização fluida, divida em múltiplas abas ou considere formato alternativo (CSV, Parquet). SheetJS gera .xlsx de 1M linhas em ~30-60 segundos em Node.js. No browser, acima de 100k, use Web Worker para não congelar UI durante conversão.
Como preservar tipos de dados (data, número decimal) no Excel?+
SheetJS infere tipos do JSON: string vira texto, number vira número, boolean vira booleano. Datas precisam cuidado: se JSON tem Date, SheetJS converte automaticamente (cellDates: true). Se tem string ISO (2026-04-20), SheetJS trata como texto por padrão — converta para Date antes de exportar ou use XLSX.utils.sheet_to_json com raw: false no read.
Posso gerar Excel com fórmulas?+
Sim, em ambas SheetJS e ExcelJS. Em SheetJS: cell.f = 'SUM(A1:A10)' define fórmula. Ao abrir no Excel, a fórmula é calculada. Útil para relatórios onde usuário pode atualizar valor de entrada e ver totais recalculados. Em exceljs, API mais rica: cell.value = { formula: 'SUM(A1:A10)', result: 1000 }.
Como adicionar formatação (cores, bordas, fonte)?+
SheetJS plain não suporta formatação rica (só estrutura). Para formatação, use SheetJS Pro (pago) ou biblioteca exceljs (grátis, open source, mais pesada). ExcelJS permite cores de células, bordas, fonte, merge, condicional formatting. Para relatórios corporate com branding, exceljs é a escolha. Para export funcional simples, SheetJS é suficiente.
Como gerar download automático do .xlsx no browser?+
SheetJS tem XLSX.writeFile(workbook, 'dados.xlsx') que dispara download automaticamente no browser. No Node.js, escreve no filesystem. Alternativamente, use XLSX.write(wb, { type: 'buffer' }) para obter Buffer e criar Blob + download manual (útil para integração com outras bibliotecas ou APIs).
Artigos relacionados
JSON vs CSV (2026): Quando Usar Cada Formato e Como Converter
Comparativo completo JSON vs CSV: diferenças técnicas, performance, casos de uso, limitações, parquet como alternativa e código de conversão entre os formatos.
Como Converter JSON para CSV em JavaScript (2026): Guia Completo com Código
Guia definitivo de conversão JSON → CSV em JavaScript: abordagens manuais, PapaParse, edge cases com arrays aninhados, encoding UTF-8 com BOM e exemplos prontos em Node.js e browser.
Como Importar CSV para Banco de Dados (2026): PostgreSQL, MySQL, SQLite e MongoDB
Guia completo de ingestão de CSV em banco: COPY do Postgres, LOAD DATA do MySQL, SQLite .import, mongoimport, estratégias para arquivos grandes e validação antes do insert.