1Eventually we should host all our images properly, but for now, drag and drop them here 👇
2
3* https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/4d90a6f7-247c-4df4-3de6-928364e10000/public
4* https://imagedelivery.net/iHX6Ovru0O7AjmyT5yZRoA/f175100b-a190-4772-7056-04c09f273a00/public
12}) {
13 const description = post?.description ?? SITE_DESCRIPTION;
14 const ogImage = new URL("/og-image.png", BLOG_URL);
15 ogImage.searchParams.append("title", title);
16
17 return (
19 <meta charSet="UTF-8" />
20 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
21 <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
22
23 <title>{title === "Val Town Blog" ? title : `${title} | Val Town Blog`}</title>
30 <meta property="og:description" content={description} />
31
32 <meta property="og:image" content={ogImage} />
33
34 {/* Twitter */}
35 <meta property="twitter:card" content="summary_large_image" />
36 <meta property="twitter:url" content={BLOG_URL} />
37 <meta property="twitter:title" content={title} />
38 <meta property="twitter:description" content={description} />
39
40 <meta property="twitter:image" content={ogImage} />
41
42 {
79 "slug": "fal",
80 "link": "/blog/fal",
81 "description": "Bringing lightning fast AI image generation to Val Town",
82 "pubDate": "Thu, 31 Oct 2024 00:00:00 GMT",
83 "author": "Steve Krouse",
13 {
14 headers: {
15 "Content-Type": "image/svg+xml",
16 },
17 },
40- Item 3
41
42### Images
43
44Images can be included using markdown syntax.
45
46## Conclusion
6---
7
8
9
10We migrated our blog to Val Town 🥳
79## A fast migration
80
81How did we migrate 65 blog posts – with images, videos, and intricate formatting and styles – to an entirely new blog in a single day?
82
83We didn't. We left them where they are, and proxy to them.
13- [Hono JSX Starter][]: Render Hono with JSX
14- [Hono Client Starter][]: Render Hono with JSX on the server and hydrate on the client
15- [SVG Starter][]: Render an SVG image response
16- [SVG PNG Starter][]: Render an SVG as a PNG image response
17- [Preact Starter][]: Render Preact on the server
18- [Preact Client Starter][]: Render Preact on the server and hydrate the app on the client
42
43<!--
44- README card image
45
46- [x] React Starter
3It's common to have code and types that are needed on both the frontend and the backend. It's important that you write this code in a particularly defensive way because it's limited by what both environments support:
4
5
6
7For example, you *cannot* use the `Deno` keyword. For imports, you can't use `npm:` specifiers, so we reccomend `https://esm.sh` because it works on the server & client. You *can* use TypeScript because that is transpiled in `/backend/index.ts` for the frontend. Most code that works on the frontend tends to work in Deno, because Deno is designed to support "web-standards", but there are definitely edge cases to look out for.
21## `favicon.svg`
22
23As of this writing Val Town only supports text files, which is why the favicon is an SVG and not an .ico or any other binary image format. If you need binary file storage, check out [Blob Storage](https://docs.val.town/std/blob/).
24
25## `components/`
73});
74
75// --- Blob Image Serving Routes ---
76
77// GET /api/images/:filename - Serve images from blob storage
78app.get("/api/images/:filename", async (c) => {
79 const filename = c.req.param("filename");
80
81 try {
82 // Get image data from blob storage
83 const imageData = await blob.get(filename);
84
85 if (!imageData) {
86 return c.json({ error: "Image not found" }, 404);
87 }
88
90 let contentType = "application/octet-stream"; // Default
91 if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) {
92 contentType = "image/jpeg";
93 } else if (filename.endsWith(".png")) {
94 contentType = "image/png";
95 } else if (filename.endsWith(".gif")) {
96 contentType = "image/gif";
97 } else if (filename.endsWith(".svg")) {
98 contentType = "image/svg+xml";
99 }
100
101 // Return the image with appropriate headers
102 return new Response(imageData, {
103 headers: {
104 "Content-Type": contentType,
107 });
108 } catch (error) {
109 console.error(`Error serving image ${filename}:`, error);
110 return c.json(
111 { error: "Failed to load image", details: error.message },
112 500,
113 );