CSV remains the lingua franca of data exchange for spreadsheets, analytics tools, and business reporting. While JSON is the developer's format of choice, analysts, accountants, and managers need CSV. Whether you're exporting API data for Excel, preparing reports for stakeholders, or loading data into pandas DataFrames, knowing how to reliably convert JSON to CSV is an essential skill.

JSON supports nested objects, arrays, heterogeneous types, and null values. CSV is a flat grid of rows and columns with string values. Converting between them means resolving these structural mismatches. A simple JSON.stringify() won't work —you need a strategy for flattening nested structures, handling arrays, and escaping special characters.

Method 1: JavaScript —Manual Conversion

Basic Flat Object Conversion

// Input JSON array



const data = [



  { "name": "Alice", "email": "[email protected]", "role": "admin" },



  { "name": "Bob", "email": "[email protected]", "role": "user" },



  { "name": "Carol", "email": "[email protected]", "role": "editor" }



];







// Convert to CSV



function jsonToCsv(data) {



  if (!data.length) return '';



  const headers = Object.keys(data[0]);



  const csvRows = [headers.join(',')];







  for (const row of data) {



    const values = headers.map(h => {



      const val = row[h] ?? '';



      return '"' + String(val).replace(/"/g, '""') + '"';



    });



    csvRows.push(values.join(','));



  }







  return csvRows.join('\n');



}







console.log(jsonToCsv(data));



// name,email,role



// "Alice","[email protected]","admin"



// "Bob","[email protected]","user"



// "Carol","[email protected]","editor"

Handling Nested Objects

// Flatten nested JSON objects for CSV



function flattenObject(obj, prefix = '') {



  const result = {};







  for (const key of Object.keys(obj)) {



    const newKey = prefix ? prefix + '.' + key : key;



    const value = obj[key];







    if (value && typeof value === 'object' && !Array.isArray(value)) {



      Object.assign(result, flattenObject(value, newKey));



    } else {



      result[newKey] = value;



    }



  }







  return result;



}







// Example: nested address



const data = [



  { "name": "Alice", "address": { "city": "NYC", "zip": "10001" } },



  { "name": "Bob", "address": { "city": "LA", "zip": "90001" } }



];







const flat = data.map(item => flattenObject(item));



console.log(jsonToCsv(flat));



// name,address.city,address.zip



// "Alice","NYC","10001"



// "Bob","LA","90001"

Handling Arrays in JSON

// When a field contains an array, join values or create separate rows



function flattenWithArrays(obj, prefix = '') {



  const result = {};







  for (const key of Object.keys(obj)) {



    const newKey = prefix ? prefix + '.' + key : key;



    const value = obj[key];







    if (Array.isArray(value)) {



      result[newKey] = value.join('; ');



    } else if (value && typeof value === 'object') {



      Object.assign(result, flattenWithArrays(value, newKey));



    } else {



      result[newKey] = value;



    }



  }







  return result;



}







// Example: user with multiple tags



const data = [



  { "name": "Alice", "tags": ["python", "go", "rust"] },



  { "name": "Bob", "tags": ["javascript"] }



];







const flat = data.map(item => flattenWithArrays(item));



console.log(jsonToCsv(flat));



// name,tags



// "Alice","python; go; rust"



// "Bob","javascript"

Download CSV in the Browser

// Create a downloadable CSV file from JSON data



function downloadCsv(data, filename = 'export.csv') {



  const csv = jsonToCsv(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 = filename;



  link.click();







  URL.revokeObjectURL(url);



}







// Usage



fetch('/api/users')



  .then(res => res.json())



  .then(data => downloadCsv(data, 'users.csv'));

Method 2: Python —Using pandas

import pandas as pd



import json







# Load JSON data



with open('data.json', 'r') as f:



    data = json.load(f)







# Convert to DataFrame



df = pd.json_normalize(data)  # Automatically flattens nested objects







# Export to CSV



df.to_csv('output.csv', index=False)







# Handle arrays by joining with a separator



# df.to_csv('output.csv', index=False, sep=',', quotechar='"', escapechar='\\')







# Or with more control:



df.to_csv('output.csv', index=False, encoding='utf-8',



          columns=['name', 'email', 'role'],  # Select columns



          na_rep='N/A')  # Replace NaN values

Method 3: Python —Without Dependencies

import json



import csv







def json_to_csv(data, output_path):



    if not data:



        return







    # Flatten nested objects



    def flatten(obj, prefix=''):



        items = {}



        for k, v in obj.items():



            key = f'{prefix}.{k}' if prefix else k



            if isinstance(v, dict):



                items.update(flatten(v, key))



            elif isinstance(v, list):



                items[key] = '; '.join(str(i) for i in v)



            else:



                items[key] = v



        return items







    flat_data = [flatten(item) for item in data]



    headers = list(flat_data[0].keys())







    with open(output_path, 'w', newline='', encoding='utf-8') as f:



        writer = csv.DictWriter(f, fieldnames=headers)



        writer.writeheader()



        writer.writerows(flat_data)







# Usage



with open('data.json') as f:



    data = json.load(f)







json_to_csv(data, 'output.csv')

Edge Cases and Gotchas

CSV fields containing commas, quotes, or newlines must be wrapped in double quotes. Double quotes within values are escaped by doubling them (""). Most CSV libraries handle this automatically, but manual implementations often miss edge cases with Unicode characters, null bytes, or Windows line endings.

Not every JSON object will have the same keys. When generating CSV headers, collect all unique keys from all objects first. Missing values should be empty strings or a placeholder like N/A —not undefined or null, which break CSV parsers.

JSON dates are often ISO 8601 strings (2026-04-29T08:00:00Z). When converting to CSV, consider formatting them for the target audience —Excel-friendly dates, locale-specific formats, or Unix timestamps depending on the use case.

For small datasets (under 10,000 rows), any method works fine. For larger datasets, pandas with chunksize parameter or streaming CSV writers will prevent memory issues. Always test with a sample of your actual data before processing the full dataset.

Quick Comparison: JavaScript vs Python

FeatureJavaScriptPython (pandas)
SetupZero dependenciespip install pandas
Nested objectsManual flatten functionjson_normalize()
Large filesStreaming with generatorschunksize parameter
Type handlingManual coercionAutomatic dtype inference
Browser exportBlob + downloadN/A (server-side only)
Best forFrontend apps, Node.js scriptsData analysis, ETL pipelines

Key Takeaways

  • Always flatten nested JSON objects before converting to CSV —flat key-value pairs are the CSV contract
  • Handle arrays by joining values with a delimiter or exploding into separate rows
  • Use pandas json_normalize() for Python —it handles flattening automatically
  • Escape special characters (commas, quotes, newlines) properly in CSV fields
  • Test with your actual data —synthetic tests miss real-world edge cases like missing keys and null values
  • For browser-based conversion, create a Blob and trigger download via a temporary anchor element