skip to Main Content

How do I trigger a file download when a button is clicked?

For testing sake, when making a GET request using <Link href="/api/generate-pdf"/> it works fine and the pdf file is saved. With the button the api is hit but the download is not triggered.

My button rendered in a Nextjs 13 Client Component:

function generatePdf() {
    fetch("/api/generate-pdf", {
      method: "POST",
      headers: {
        "Content-type": "application/pdf",
      },
      body: JSON.stringify(store),
    })
  }

<Button onClick={generatePdf} type="button">
    Generate PDF
</Button>

API Route Handler:

export async function POST(request: Request) {
  console.log("/api/generate-pdf POST Request")
  try {
    // ...
    // generate the pdf file
    const generatedPdf = doc.output("blob")
    const filename = "pac.pdf"
    const res = new Response(generatedPdf, {
      status: 200,
      headers: {
        "Content-Type": "application/pdf",
        "content-disposition": `attachment; filename="${filename}"`,
      },
    })
    return res
  } catch (error) {
    console.log(error)
    return NextResponse.json("Internal Error", { status: 500 })
  }
}

2

Answers


  1. The issue you’re probably encountering is due to the fact that the fetch API does not handle file downloads in the same way a direct link does. When you click the button, the fetch API makes the request and receives the response, but it’s not triggering the file download that a direct link would.

    To solve this, you can modify your generatePdf function to create a new Blob object from the response and then create a new object URL for that Blob. One way which I can think of after that is you can programmatically click a hidden anchor element that points to this object URL to trigger the file download(using a’s download attribute) something like below:

    async function generatePdf() {
      const response = await fetch("/api/generate-pdf", {
        method: "POST",
        headers: {
          "Content-type": "application/pdf",
        },
        body: JSON.stringify(store),
      });
    
      const blob = await response.blob(); // create a new Blob object.
      const url = window.URL.createObjectURL(blob); // create a new object url
      const a = document.createElement("a"); // create a new anchor element
      a.href = url; // set its href to the object URL
      a.download = "yourPDF.pdf";  // set its download attribute to the deisred filename.
      a.click(); // programmatically clicking the anchor element to trigger the file download.
    }
    
    Login or Signup to reply.
  2. When you’re making an AJAX request to download a file (like using the fetch API in your case), the browser doesn’t automatically know how to handle the file once it receives the data. To trigger a download, you’ll need to do some extra handling on the client side once the file data is received.

    function generatePdf() {
        fetch("/api/generate-pdf", {
          method: "POST",
          headers: {
            "Content-type": "application/json",  // Make sure this matches what you're sending
          },
          body: JSON.stringify(store),
        })
        .then(response => response.blob())
        .then(blob => {
          const url = window.URL.createObjectURL(blob);
          const a = document.createElement('a');
          a.href = url;
          a.download = 'pac.pdf';
          document.body.appendChild(a);
          a.click();
          a.remove();
        })
        .catch((error) => {
          console.error("There was an error!", error);
        });
    }
    

    Hear’s a breakdown of the additional steps:

    1.After getting the response, you transform it into a blob.
    2.Create a URL from that blob using URL.createObjectURL().
    3.Create a hidden anchor () element, set the href to the blob URL, and programmatically click it. This causes the browser to download the file.
    4.Remove the anchor element after triggering the download.

    Also, please note that the Content-Type header in your fetch method should be "application/json" since you’re sending JSON data. Make sure your server can handle that type of content when receiving the request.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search