In my previous projects codewrite and codewrite-2. There was a need for a download button that downloaded some text. The projects are built using react and I will show you how you can do the same.
Here is a sample download button using just plain html,css and js:
For the code the html, css is very simple. I am also using bootstrap but you can style them however you want:
<textarea id="downloadableText" style="width:100%;padding-left:0.5rem">Your text here</textarea>
<div style="width:100%; display:flex; flex-direction:row; gap:0.25rem;">
<input type="text" id="fileName" value="fileName" required style="width:100%; padding-left:0.5rem;"/>
<button class="btn btn-dark" onclick="downloadText()" style="gap:0.5rem;display:flex;flex-direction:row; align-items:center;">
<!-- Download Icon -->
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512">
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path style="fill:rgb(220,220,220);" d="M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32V274.7l-73.4-73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l128 128c12.5 12.5 32.8 12.5 45.3 0l128-128c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L288 274.7V32zM64 352c-35.3 0-64 28.7-64 64v32c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V416c0-35.3-28.7-64-64-64H346.5l-45.3 45.3c-25 25-65.5 25-90.5 0L165.5 352H64zm368 56a24 24 0 1 1 0 48 24 24 0 1 1 0-48z"/>
</svg>
Download
</button>
</div>
The actual code is the javascript:
function downloadText()
{
let downloadableValue=document.getElementById("downloadableText").innerText;
let fileName=document.getElementById("fileName").value;
const link = document.createElement('a');
const content = new Blob([downloadableValue], { type: `text;charset=utf-8` });
link.href = URL.createObjectURL(content);
link.download = fileName;
link.click();
URL.revokeObjectURL(link.href);
link.remove();
}
let downloadableValue=document.getElementById("downloadableText").innerText;
Reading the text to download.
let fileName=document.getElementById("fileName").value
Reading the value of the input to use it as filename when downloading.
const link = document.createElement('a');
We create a temporary link element that will be used to download the resource.
const content = new Blob([downloadableValue], { type:
text;charset=utf-8 });
We then create a use Blob class used to create immutable, raw data (binary large object) file.The array contains the data to be included in the Blob. downloadableValue could be a string, typed array, or other binary data. The { type: 'text;charset=utf-8' }
specifies the MIME type of the Blob's content.
You can find about common MIME Types here.
link.href = URL.createObjectURL(content);
When we create a Blob (e.g., a file, image, or other binary data) and use URL.createObjectURL(blob) to generate a temporary URL that points to the Blob's data, it allocates resources in the browser to keep that Blob accessible. The url of the resource is then used as the href of the link
element.
URL.revokeObjectURL(link.href);
The generated object URL remains valid until you explicitly revoke it with URL.revokeObjectURL(). If we don’t revoke it, those resources stay allocated, leading to memory leaks over time, especially if we create many Blob URLs.
Creating this in react is also the same, we can use the same function and attach it to the onClick
handler. We can create controlled input or simply use an uncontrolled input for the text inputs.
The function will remain the same.
const downloadText = () => {
const downloadableValue = fileContent;
const link = document.createElement('a');
const content = new Blob([downloadableValue], {
type: 'text/plain;charset=utf-8',
});
link.href = URL.createObjectURL(content);
link.download = fileName;
link.click();
URL.revokeObjectURL(link.href);
link.remove();
};
The component code will be:
const [fileContent, setFileContent] = useState('Your text here');
const [fileName, setFileName] = useState('fileName');
return (
<>
<textarea
id='downloadableText'
style={{ width: '100%', paddingLeft: '0.5rem' }}
value={fileContent}
onChange={e => setFileContent(e.target.value)}
/>
<div
style={{
width: '100%',
display: 'flex',
flexDirection: 'row',
gap: '0.25rem',
}}
>
<input
type='text'
id='fileName'
value={fileName}
onChange={e => setFileName(e.target.value)}
required
style={{ width: '100%', paddingLeft: '0.5rem' }}
/>
<button
className='btn btn-dark'
onClick={downloadText}
style={{
gap: '0.5rem',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
}}
>
<svg
xmlns='http://www.w3.org/2000/svg'
height='16'
width='16'
viewBox='0 0 512 512'
>
<path
style={{ fill: 'rgb(220,220,220)' }}
d='M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32V274.7l-73.4-73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l128 128c12.5 12.5 32.8 12.5 45.3 0l128-128c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L288 274.7V32zM64 352c-35.3 0-64 28.7-64 64v32c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V416c0-35.3-28.7-64-64-64H346.5l-45.3 45.3c-25 25-65.5 25-90.5 0L165.5 352H64zm368 56a24 24 0 1 1 0 48 24 24 0 1 1 0-48z'
/>
</svg>
Download
</button>
</div>
</>
);
The final code will look like:
import { useState } from 'react';
export function App() {
return <DownloadComponent />;
}
const DownloadComponent = () => {
const [fileContent, setFileContent] = useState('Your text here');
const [fileName, setFileName] = useState('fileName');
const downloadText = () => {
const downloadableValue = fileContent;
const link = document.createElement('a');
const content = new Blob([downloadableValue], {
type: 'text/plain;charset=utf-8',
});
link.href = URL.createObjectURL(content);
link.download = fileName;
link.click();
URL.revokeObjectURL(link.href);
link.remove();
};
return (
<div>
<textarea
id='downloadableText'
style={{ width: '100%', paddingLeft: '0.5rem' }}
value={fileContent}
onChange={e => setFileContent(e.target.value)}
/>
<div
style={{
width: '100%',
display: 'flex',
flexDirection: 'row',
gap: '0.25rem',
}}
>
<input
type='text'
id='fileName'
value={fileName}
onChange={e => setFileName(e.target.value)}
required
style={{ width: '100%', paddingLeft: '0.5rem' }}
/>
<button
className='btn btn-dark'
onClick={downloadText}
style={{
gap: '0.5rem',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
}}
>
<svg
xmlns='http://www.w3.org/2000/svg'
height='16'
width='16'
viewBox='0 0 512 512'
>
<path
style={{ fill: 'rgb(220,220,220)' }}
d='M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32V274.7l-73.4-73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l128 128c12.5 12.5 32.8 12.5 45.3 0l128-128c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L288 274.7V32zM64 352c-35.3 0-64 28.7-64 64v32c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V416c0-35.3-28.7-64-64-64H346.5l-45.3 45.3c-25 25-65.5 25-90.5 0L165.5 352H64zm368 56a24 24 0 1 1 0 48 24 24 0 1 1 0-48z'
/>
</svg>
Download
</button>
</div>
</div>
);
};
export default DownloadComponent;
Check out my other blogs if you found this useful.
Happy coding 🫡