-
Notifications
You must be signed in to change notification settings - Fork 26.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[NEXT-1089] Custom fonts in ImageResponse
not working in App Dir
#48081
Comments
I can repro this on latest canary ( |
Also had this same issue |
I'm having the same issue; as a workaround, you can do this: async function getFont(): Promise<Buffer> {
const url = process.env.APP_URL
return new Promise((resolve, reject) => {
http.get(`${url}/fonts/Font.ttf`, (res) => {
const chunks: any[] = []
res.on('data', c => chunks.push(c))
res.on('end', () =>
resolve(Buffer.concat(chunks))
)
}).on('error', (err) => {
reject(err)
})
})
}
This is pretty hacky, it would be nice if we could use |
Hi, you’re using the default nodejs runtime, you can use fs read to access the font data in nodejs runtime. Check out the nodejs example list here Or you can just add |
Can you try to move the font load inside the route handler it resolves the problem for me @surjithctly const font = fetch(new URL('Inter-Regular.woff', import.meta.url)).then((res) =>
res.arrayBuffer()
); |
Whenever I try this, the file ends up not existing when deploying to Vercel. And when using the edge runtime the bundle size ends up being larger than 1 MB. |
@huozhi For me, moving into the handler doesn't fix it. But |
@lukadev-0 can you check the nodejs usage of og section if you want to use nodejs runtime, it's just using fs operations instead of |
@surjithctly yeah the same as above, sorry for the confusion of posting the comments separately. If you want to use edge runtime, you need to move it to the font assets loading handler function, and also you need to enable edge runtime |
I tried that however the files didn't get included in the deployment |
@lukadev-0 could you provide a reproduction? Can take a look on that to see what's going wrong |
Here you go: https://github.com/lukadev-0/next-og-image-font-repro Whenever fetching the open graph image the following error is thrown:
|
Doesn't look like the font is in the |
@huozhi I ran into this issue as well. Had the same exact config in the
|
@lukadev-0 Can you try the latest canary it resolves the deployment issue for me |
@ekqt would you mind filing another issue with a reproduction for that as it's not related to the original problem of this issue |
I've tried with 13.4 (its newer than canary) and it worked. Thanks! |
@huozhi Turns out it doesn't work when there's a dynamic data fetch. (I've updated my reproduction) export default async function og() {
const res = await fetch("https://jsonplaceholder.typicode.com/todos/1", {
cache: "no-store",
}); |
Yeah can repro that, we take a look. |
ImageResponse
not working in App DirImageResponse
not working in App Dir
I changed my font and started to get this strange error. If I remove the fonts[], it works.
UPDATE: Seems it cannot handle variable font yet? Replaced with a static one and worked again. |
Yeah, using the exact example in the doc in the latest/canary Next.js version will still fail with the above error.
And this workaround no longer works as well. Edit const getInterSemiBold = async () => {
const response = await fetch(
new URL('./Inter-SemiBold.ttf', import.meta.url)
);
const interSemiBold = await response.arrayBuffer();
return interSemiBold;
}
// ...
new ImageResponse(
<OgImage />,
{
fonts: [
{
name: 'Inter',
data: await getInterSemiBold(),
style: 'normal',
weight: 400,
},
],
}
) |
I was getting the |
Yep, loading the font in a function as described by @mwskwong does the trick |
Confirmed, got it working with:
|
The weird thing is, that function getFont does not work with a argument const getFont = async (font: string) => {
const res = await fetch(new URL(`./${font}.ttf`, import.meta.url));
return await res.arrayBuffer();
};
Following code works: const getFont = async (url: URL) => {
const res = await fetch(url);
return await res.arrayBuffer();
}; and then await getFont(new URL('./Inter-Medium.ttf', import.meta.url)) |
Link doesn't work. Looks like this example was silently removed. Proof: web.archive.org. There is same last updated date, but different content. Example from old doc, it works for node runtime: const font = fs.promises.readFile(
path.join(fileURLToPath(import.meta.url), '../../assets/TYPEWR__.ttf'),
);
const image = fs.promises.readFile(
path.join(fileURLToPath(import.meta.url), '../../assets/image.png'),
); |
Moved around the things based on the suggestions. import { ImageResponse } from "next/server";
const basePath = "hectorsosa.me/";
const title = "Hector Sosa";
const param = "";
export const runtime = "edge";
export const alt = "Hector Sosa";
export const size = {
width: 1200,
height: 630,
};
export const contentType = "image/png";
export default async function Image() {
const groteskRegular = await fetch(
new URL("./SchibstedGrotesk-Regular.ttf", import.meta.url)
).then((res) => res.arrayBuffer());
const groteskSemibold = await fetch(
new URL("./SchibstedGrotesk-Semibold.ttf", import.meta.url)
).then((res) => res.arrayBuffer());
return new ImageResponse(
(
<div
style={{
fontWeight: 600,
background: "rgb(250, 250, 250)",
width: "100%",
height: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: 120,
textAlign: "center",
}}
>
<div
style={{
position: "absolute",
width: size.width / 2,
height: size.height / 1.5,
top: "-50%",
left: "60%",
opacity: "0.25",
backgroundImage: `url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%239C92AC' fill-opacity='0.29'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")`,
}}
></div>
<h1
style={{
fontSize: 128,
lineHeight: 1,
letterSpacing: "-6px",
color: "rgb(23, 23, 23)",
margin: "6px",
}}
>
{title}
</h1>
<p
style={{
fontSize: 40,
lineHeight: 1,
letterSpacing: "-1.5px",
color: "rgb(64, 64, 64)",
margin: 0,
}}
>
{basePath + param}
</p>
</div>
),
{
...size,
debug: false,
fonts: [
{
name: "Grotesk",
data: groteskRegular,
style: "normal",
weight: 400,
},
{
name: "Grotesk",
data: groteskSemibold,
style: "normal",
weight: 600,
},
],
}
);
} The build succeeds locally but fails at deployment. I'm running into this issue:
|
Guys, I don't know why!!! But using |
Folks, make sure to use static font. Variable font was making it error for me. |
When implementing `opengraph-image` in my [personal-site-project](https://github.com/kylemcd/personal-site). I was consistently running into issues where custom fonts would either only work locally or only work on vercel. To me it seemed like differences in pathing in `edge` vs `nodejs` runtimes. After digging around I found issue #48081, more specifically [this comment](#48081 (comment)) where moving the `fetch` for the font into the `Image` function solved the issue. I'm not sure if this is 100% the correct fix, or if this is an issue that needs to be solved in another way. If that's not the case this PR updates the documentation around `opengraph-image` to have the fetch for custom fonts inside of the `Image` function.
In the past few rounds of improving metadata image routes bundling, we have improved the bundling strategy and also updated [the usage tutorial of using custom fonts in og image routes](https://vercel.com/docs/functions/edge-functions/og-image-generation/og-image-examples) which should load the font in the image route handler. Adding some tests to ensure custom fonts are working with metadata Closes #48081
I created this helper: import type { Font } from "satori";
export default async function getFonts(): Promise<Font[]> {
// This is unfortunate but I can't figure out how to load local font files
// when deployed to vercel.
const [interRegular, interMedium, interSemiBold, interBold] =
await Promise.all([
fetch(`https://rsms.me/inter/font-files/Inter-Regular.woff`).then((res) =>
res.arrayBuffer()
),
fetch(`https://rsms.me/inter/font-files/Inter-Medium.woff`).then((res) =>
res.arrayBuffer()
),
fetch(`https://rsms.me/inter/font-files/Inter-SemiBold.woff`).then(
(res) => res.arrayBuffer()
),
fetch(`https://rsms.me/inter/font-files/Inter-Bold.woff`).then((res) =>
res.arrayBuffer()
),
]);
return [
{
name: "Inter",
data: interRegular,
style: "normal",
weight: 400,
},
{
name: "Inter",
data: interMedium,
style: "normal",
weight: 500,
},
{
name: "Inter",
data: interSemiBold,
style: "normal",
weight: 600,
},
{
name: "Inter",
data: interBold,
style: "normal",
weight: 700,
},
];
} And then used it whenever I needed fonts in an opengraph-image.tsx file: return new ImageResponse(..., {
fonts: await getFonts(),
}); I feel bad fetching from a CDN but I couldn't get anything else to work. Here's what I was seeing:
So fetching from a CDN and using runtime=node is the only solution that works for dynamic routes deployed to vercel. |
This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you. |
Verify canary release
Provide environment information
Operating System: Platform: darwin Arch: arm64 Version: Darwin Kernel Version 22.1.0: Sun Oct 9 20:14:30 PDT 2022; root:xnu-8792.41.9~2/RELEASE_ARM64_T8103 Binaries: Node: 18.12.0 npm: 8.19.2 Yarn: 1.22.18 pnpm: 7.25.1 Relevant packages: next: 13.3.1-canary.1 eslint-config-next: N/A react: 18.2.0 react-dom: 18.2.0
Which area(s) of Next.js are affected? (leave empty if unsure)
App directory (appDir: true), Data fetching (gS(S)P, getInitialProps), Metadata (metadata, generateMetadata, next/head, head.js)
Link to the code that reproduces this issue
https://stackblitz.com/edit/github-f6jt4y?file=app%2Fopengraph-image.js
To Reproduce
Describe the Bug
While importing custom fonts, it throws the following error.
Expected Behavior
It should not throw error. Instead it should load properly.
Which browser are you using? (if relevant)
Any
How are you deploying your application? (if relevant)
No response
NEXT-1089
The text was updated successfully, but these errors were encountered: