anne-nygard-yqNtaSnxcSU-unsplash
#javascript
#typescript
#bun
#jsx
#ssg
#performance

Optimizing Images for our JSX+Bun SSG 2024-01-22

NOTE: This will NOT work with React!

In our previous lesson we created a static site generator with Bun and JSX. Please make sure that you go through that lesson before continuing here. With it's superpower of supporting async code and the fact that it works on the server side allow us to do things ordinary React devs could only dream of. On the other hand, we can't do any of the client side stuff they do on a day to day basis. But that'd be a topic of another article. Now, let's take a look at our code, shall we?

Before we move on, we need to install something called sharp. It's a Node-API module that is used to convert large images in common formats to smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions.

Terminal

$ bun i sharp

Now, here is the whole component. The steps are described in comments, but overall what it does is

  • Turn the image to webp format with sligtly decreased quality,
  • Resize it to the width provided via a prop,
  • Save the generated image to our dist directory,
  • Return an img tag that uses the new image as a source.

./src/components/image.tsx

import sharp from "sharp"

type P = { src: string; alt: string; width: number; class?: string }
export const Image = async ({ src, alt = src, class: cls = "", width }: P) => {
	// Grab the extension of the original file.
	const extension = src.substring(src.lastIndexOf("."))
	// Create a full path that leads to our images directory.
	const originalPath = `img/${src}`
	// Create a new path where the formatted image is going to be saved.
	const newPath = src.replace(extension, ".wepb")

	// Transform image to `.webp`, resize it to given width, and save it as a file under our new path.
	await sharp(originalPath).webp({ quality: 80 }).resize(width).toFile(`dist/${newPath}`)

	// Return a component that renders the image from the new path.
	return <img src={newPath} alt={alt} class={` ${cls}`} />
}

Now we can use this image component on our pages. Grap yourself an image, put it somewhere and provide its path with the src prop. Don't forget to specify the width so that the image is resized correctly.

./src/pages/index.tsx

import { Image } from "../components/image"

export default function Index() {
	return (
		<html lang="en">
			<head>
				<title>Bun, JSX and Orlowdev</title>
			</head>
			<body>
				<main>
					<h1>My image</h1>
					<Image src="./static/my-image.png" width={1024} />
				</main>
			</body>
		</html>
	)
}