Download button in next 14
Unanswered
Yellowstripe scad posted this in #help-forum
Yellowstripe scadOP
Hey, i need to download a file from the frontend but can't figure it out in next 14
import { getNewsletter } from "./actions";
async function handleSubmit(formData: FormData) {
"use server";
const topic = formData.get("topic");
if (!topic) {
throw new Error("Topic is required");
}
const { blob, filename } = await getNewsletter(topic.toString());
download(blob, filename);
}
function download(blob: Blob, filename: string) {
"use client";
const link = document.createElement("a");
const url = window.URL.createObjectURL(blob);
link.href = url;
link.download = filename;
link.click();
}
The download function is unable to access the window object
import { getNewsletter } from "./actions";
async function handleSubmit(formData: FormData) {
"use server";
const topic = formData.get("topic");
if (!topic) {
throw new Error("Topic is required");
}
const { blob, filename } = await getNewsletter(topic.toString());
download(blob, filename);
}
function download(blob: Blob, filename: string) {
"use client";
const link = document.createElement("a");
const url = window.URL.createObjectURL(blob);
link.href = url;
link.download = filename;
link.click();
}
The download function is unable to access the window object
6 Replies
@Yellowstripe scad Hey, i need to download a file from the frontend but can't figure it out in next 14
import { getNewsletter } from "./actions";
async function handleSubmit(formData: FormData) {
"use server";
const topic = formData.get("topic");
if (!topic) {
throw new Error("Topic is required");
}
const { blob, filename } = await getNewsletter(topic.toString());
download(blob, filename);
}
function download(blob: Blob, filename: string) {
"use client";
const link = document.createElement("a");
const url = window.URL.createObjectURL(blob);
link.href = url;
link.download = filename;
link.click();
}
The download function is unable to access the window object
I think its better to do with route handler like this
because the arguments and return value of server actions must be serializable.
export async function GET(req: Request) {
const formData = await req.formData();
const topic = formData.get("topic");
if (!topic) {
return new Response("Topic is required", {status: 400});
}
const { blob, filename } = await getNewsletter(topic.toString());
return new Response(blob, {
headers: {
"Content-disposition": `attachment; filename=${filename}`,
},
});
}because the arguments and return value of server actions must be serializable.
Yellowstripe scadOP
Hey, can you assist me, Im new to next and can't figure out how to fix it
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import Navbar from "../Navbar/Navbar";
import { authOptions } from "../api/auth/[...nextauth]/route";
import { getNewsletter } from "./actions";
async function handleSubmit(formData: FormData) {
"use server";
const topic = formData.get("topic");
if (!topic) {
throw new Error("Topic is required");
}
const { blob, filename } = await getNewsletter(topic.toString());
download(blob, filename);
}
function download(blob: Blob, filename: string) {
"use client";
const link = document.createElement("a");
const url = window.URL.createObjectURL(blob);
link.href = url;
link.download = filename;
link.click();
}
export default async function NewsletterPage() {
const session = await getServerSession(authOptions);
if (!session) {
redirect("/");
}
return (
<div>
<Navbar />
<div className="hero min-h-screen bg-base-200">
<div className="hero-content text-center">
<form className="max-w-md" action={handleSubmit}>
<h1 className="text-5xl text-green-500 font-bold"> Newsletter</h1>
<input
required
name="topic"
type="text"
placeholder="Enter newsletter topic"
className="input text-black input-bordered input-lg w-full max-w-xs my-6"
/>
<button className="btn btn-primary" type="submit">
Generate
</button>
</form>
</div>
</div>
</div>
);
}@Ray
Thanks
@Ray I think its better to do with route handler like this
ts
export async function GET(req: Request) {
const formData = await req.formData();
const topic = formData.get("topic");
if (!topic) {
return new Response("Topic is required", {status: 400});
}
const { blob, filename } = await getNewsletter(topic.toString());
return new Response(blob, {
headers: {
"Content-disposition": `attachment; filename=${filename}`,
},
});
}
because the arguments and return value of server actions must be serializable.
create a route handler like this
// app/api/newsletter/route.ts
export async function POST(req: Request) {
const formData = await req.formData();
const topic = formData.get("topic");
if (!topic) {
return new Response("Topic is required", {status: 400});
}
const { blob, filename } = await getNewsletter(topic.toString());
return new Response(blob, {
headers: {
"Content-disposition": `attachment; filename=${filename}`,
},
});
}and change your form to this
<form className="max-w-md" method="post" action='/api/newsletter'>
<h1 className="text-5xl text-green-500 font-bold"> Newsletter</h1>
<input
required
name="topic"
type="text"
placeholder="Enter newsletter topic"
className="input text-black input-bordered input-lg w-full max-w-xs my-6"
/>
<button className="btn btn-primary" type="submit">
Generate
</button>
</form>