Ivan Revzin

Download JSON formed in Browser

In my todo app I stored all data in a JSON in LocalStorage.

To move from my old domain to a new one I needed export/import functionality.

To do the export I used JSON.stringify, btoa and an anchor tag with href pointing to a Data URI scheme:

function ExportTodos({ todos = [] }) {
  const todos64 = [todos]
    .map((val) => JSON.stringify(val)) // js object to string
    .map(btoa) // string to base64

  return (
    <a href={`data:application/json;base64,${todos64}`}>

But the btoa function crushed:

btoa('Привет, world!')
// Uncaught DOMException: String contains an invalid character

I started to look for the reason. Turns out btoa has a unicode problem.

I found some solutions:

Using Blob there were 2 ways to form a download URL:

  1. using URL.createObjectURL()
  2. using FileReader.readAsDataURL

The first method was simpler so I used it. But there was a potential issue with memory, but simple useEffect's cleanup function solved it.

function ExportTodos({ todos = [] }) {
  const [downloadURL, setDownloadURL] = useState('');

  useEffect(() => {
    const blob = new Blob(
      { type: 'application/json' }
    const downloadURL = URL.createObjectURL(blob);

    return () => {
  }, [todos]);

  return (

Full ExportTodos component file is here.