og-image.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import { ImageResponse } from "next/og";
  2. import { readFile } from "node:fs/promises";
  3. import { join } from "node:path";
  4. export { getPageTitle } from "@/lib/page-titles";
  5. // Cache font data in memory after first load
  6. let fontCache: { geistRegular: Buffer; geistPixelSquare: Buffer } | null = null;
  7. async function loadFonts() {
  8. if (fontCache) return fontCache;
  9. const [geistRegular, geistPixelSquare] = await Promise.all([
  10. readFile(join(process.cwd(), "public/Geist-Regular.ttf")),
  11. readFile(join(process.cwd(), "public/GeistPixel-Square.ttf")),
  12. ]);
  13. fontCache = { geistRegular, geistPixelSquare };
  14. return fontCache;
  15. }
  16. export async function renderOgImage(title: string) {
  17. const { geistRegular, geistPixelSquare } = await loadFonts();
  18. return new ImageResponse(
  19. <div
  20. style={{
  21. width: "100%",
  22. height: "100%",
  23. display: "flex",
  24. flexDirection: "column",
  25. backgroundColor: "black",
  26. padding: "60px 80px",
  27. }}
  28. >
  29. <div
  30. style={{
  31. display: "flex",
  32. alignItems: "center",
  33. gap: "16px",
  34. }}
  35. >
  36. <svg width="36" height="36" viewBox="0 0 16 16" fill="white">
  37. <path fillRule="evenodd" clipRule="evenodd" d="M8 1L16 15H0L8 1Z" />
  38. </svg>
  39. <span
  40. style={{
  41. fontSize: 36,
  42. color: "#666",
  43. fontFamily: "Geist",
  44. fontWeight: 400,
  45. }}
  46. >
  47. /
  48. </span>
  49. <span
  50. style={{
  51. fontSize: 36,
  52. fontFamily: "Geist Pixel Square",
  53. fontWeight: 500,
  54. color: "white",
  55. }}
  56. >
  57. json-render
  58. </span>
  59. </div>
  60. <div
  61. style={{
  62. display: "flex",
  63. flex: 1,
  64. flexDirection: "column",
  65. alignItems: "center",
  66. justifyContent: "center",
  67. }}
  68. >
  69. {title.split("\n").map((line, i) => (
  70. <span
  71. key={i}
  72. style={{
  73. fontSize: 72,
  74. fontFamily: "Geist",
  75. fontWeight: 400,
  76. color: "white",
  77. letterSpacing: "-0.02em",
  78. textAlign: "center",
  79. lineHeight: 1.2,
  80. }}
  81. >
  82. {line}
  83. </span>
  84. ))}
  85. </div>
  86. </div>,
  87. {
  88. width: 1200,
  89. height: 630,
  90. fonts: [
  91. {
  92. name: "Geist",
  93. data: geistRegular.buffer as ArrayBuffer,
  94. style: "normal",
  95. weight: 400,
  96. },
  97. {
  98. name: "Geist Pixel Square",
  99. data: geistPixelSquare.buffer as ArrayBuffer,
  100. style: "normal",
  101. weight: 500,
  102. },
  103. ],
  104. },
  105. );
  106. }