To download a file in React, create a Blob from your data, generate an object URL with URL.createObjectURL, attach it to a temporary <a> element, and programmatically click it. No libraries needed. The same logic works in Vue, Svelte, and Angular.
There are websites that let users download CSV or JSON data as a file. This functionality can be quite useful, as users can download the data for further processing or to share it. In this article, you will learn how to add the functionality that will allow users to export a table in React and download it in JSON and CSV formats.
You can find the full code example in the GitHub repo. The code is also available in the interactive CodeSandbox example below. However, note that the files won't be downloaded there due to the fact that the code in the CodeSandbox runs in an isolated environment.
Project Setup
First, let's create a new React project using Vite.
npm init vite@latest csv-json-files-download -- --template react
After the project is created, cd into it to install dependencies by running npm install and then start the dev server with npm run dev.
Next, we need to modify the App.jsx and App.css files.
src/App.jsx
import React from 'react'
function App() {
return (
<div className='App'>
<h1>How to download CSV and JSON files in React</h1>
</div>
)
}
export default App
src/App.css
.App {
max-width: 40rem;
margin: 2rem auto;
}
That's enough for the initial setup. Let's start by adding functionality to export to JSON.
Export to JSON
Let's start by creating a file with users data that will be used for downloading a file and rendering a table.
src/users.json
{
"users": [
{
"id": 1,
"name": "Caitlyn",
"surname": "Kerluke",
"age": 24
},
{
"id": 2,
"name": "Rowan ",
"surname": "Nikolaus",
"age": 45
},
{
"id": 3,
"name": "Kassandra",
"surname": "Haley",
"age": 32
},
{
"id": 4,
"name": "Rusty",
"surname": "Arne",
"age": 58
}
]
}
Next, we need to update the App component to utilise the users data and display it in a table. Besides that, we will add a button to trigger the download. Below you can see the code for the App.jsx component. Besides the component. we have two functions: exportToJson and downloadFile. The former one calls the latter with appropriate arguments. The downloadFile function accepts an object as a parameter and expects three properties:
- data
- fileName
- fileType
The data and fileType are used to create a blob that is downloaded. After that, we create an anchor element and dispatch a click event on it.
src/App.jsx
import React from 'react'
import './App.css'
import usersData from './users.json'
const downloadFile = ({ data, fileName, fileType }) => {
// Create a blob with the data we want to download as a file
const blob = new Blob([data], { type: fileType })
// Create an anchor element and dispatch a click event on it
// to trigger a download
const a = document.createElement('a')
a.download = fileName
a.href = window.URL.createObjectURL(blob)
const clickEvt = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true,
})
a.dispatchEvent(clickEvt)
a.remove()
}
const exportToJson = e => {
e.preventDefault()
downloadFile({
data: JSON.stringify(usersData.users),
fileName: 'users.json',
fileType: 'text/json',
})
}
function App() {
return (
<div className='App'>
<h1>How to download CSV and JSON files in React</h1>
<table className='usersTable'>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Surname</th>
<th>Age</th>
</tr>
</thead>
<tbody>
{usersData.users.map(user => {
const { id, name, surname, age } = user
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{surname}</td>
<td>{age}</td>
</tr>
)
})}
</tbody>
</table>
<div className='actionBtns'>
<button type='button' onClick={exportToJson}>
Export to JSON
</button>
</div>
</div>
)
}
export default App
We can add a few styles, so the table looks a bit nicer.
src/App.css
.App {
max-width: 40rem;
margin: 2rem auto;
}
.usersTable th,
.usersTable td {
padding: 0.4rem 0.6rem;
text-align: left;
}
.actionBtns {
margin-top: 1rem;
}
.actionBtns button + button {
margin-left: 1rem;
}
Great, now you should be able to download the users data as a JSON file by clicking on the Export to JSON button. Next, we will add Export to CSV functionality.
TypeScript version
Here is the downloadFile function with TypeScript types. Instead of a union like 'text/json' | 'text/csv', the interface uses a generic constrained to string. This keeps the function reusable for any MIME type while TypeScript still narrows the type to the exact literal you pass in — no changes needed if you later want to support 'text/plain' or 'application/pdf'.
interface DownloadFileOptions<T extends string = string> {
data: string
fileName: string
fileType: T
}
const downloadFile = <T extends string>({ data, fileName, fileType }: DownloadFileOptions<T>) => {
const blob = new Blob([data], { type: fileType })
const a = document.createElement('a')
a.download = fileName
a.href = window.URL.createObjectURL(blob)
const clickEvt = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true,
})
a.dispatchEvent(clickEvt)
a.remove()
}
Export to CSV
We need another button that will be used to export data to a CSV file. Besides that, we also need a handler for it. The usersData is in the JSON format, so we will need to convert it to the CSV format, before passing it to the downloadFile function.
src/App.jsx
import React from 'react'
import './App.css'
import usersData from './users.json'
const downloadFile = ({ data, fileName, fileType }) => {
const blob = new Blob([data], { type: fileType })
const a = document.createElement('a')
a.download = fileName
a.href = window.URL.createObjectURL(blob)
const clickEvt = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true,
})
a.dispatchEvent(clickEvt)
a.remove()
}
const exportToJson = e => {
e.preventDefault()
downloadFile({
data: JSON.stringify(usersData.users),
fileName: 'users.json',
fileType: 'text/json',
})
}
const exportToCsv = e => {
e.preventDefault()
// Headers for each column
let headers = ['Id,Name,Surname,Age']
// Convert users data to a csv
let usersCsv = usersData.users.reduce((acc, user) => {
const { id, name, surname, age } = user
acc.push([id, name, surname, age].join(','))
return acc
}, [])
downloadFile({
data: [...headers, ...usersCsv].join('\n'),
fileName: 'users.csv',
fileType: 'text/csv',
})
}
function App() {
return (
<div className='App'>
<h1>How to download CSV and JSON files in React</h1>
<table className='usersTable'>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Surname</th>
<th>Age</th>
</tr>
</thead>
<tbody>
{usersData.users.map(user => {
const { id, name, surname, age } = user
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{surname}</td>
<td>{age}</td>
</tr>
)
})}
</tbody>
</table>
<div className='actionBtns'>
<button type='button' onClick={exportToJson}>
Export to JSON
</button>
<button type='button' onClick={exportToCsv}>
Export to CSV
</button>
</div>
</div>
)
}
export default App
Wrap up
That covers the download logic. The downloadFile function itself is framework-agnostic: you can use the same Blob and anchor approach in Vue, Svelte, or Angular without any changes.

React - The Road To Enterprise
Do you want to know how to create scalable and maintainable React apps with architecture that actually works?
Find out more
