Skip to content

Commit de39caa

Browse files
perf: serve MDX screenshots as WebP through Next's image optimizer
The docs embed ~360 PNG screenshots from GCS (sourcegraphstatic.com and storage.googleapis.com/sourcegraph-assets), uncompressed and with a one-hour cache. Route allow-listed PNG/JPEG sources through /_next/image, which converts to WebP, caps width at 1920px and caches at Vercel's edge for a week. The zoom modal keeps the original full-resolution file. Amp-Thread-ID: https://ampcode.com/threads/T-01a0ac9d-704f-73fe-8211-1b3e5840969f Co-authored-by: Amp <amp@ampcode.com>
1 parent 974b0fc commit de39caa

3 files changed

Lines changed: 31 additions & 2 deletions

File tree

‎docs.config.js‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
const config = {
2-
DOCS_LATEST_VERSION: '7.7'
2+
DOCS_LATEST_VERSION: '7.7',
3+
// Remote image origins that Next's optimizer may fetch, convert to WebP and cache.
4+
OPTIMIZED_IMAGE_ORIGINS: [
5+
'https://sourcegraphstatic.com/',
6+
'https://storage.googleapis.com/sourcegraph-assets/',
7+
'https://storage.googleapis.com/changelog-static-assets-prod/'
8+
]
39
};
410

511
module.exports = config;

‎next.config.js‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const {withContentlayer} = require('next-contentlayer2');
2+
const {OPTIMIZED_IMAGE_ORIGINS} = require('./docs.config');
23
/** @type {import('next').NextConfig} */
34

45
// in prod, we serve the docs from sourcegraph.com/docs, and this requires special config on the GFE side
@@ -18,6 +19,12 @@ const nextConfig = {
1819
env: {
1920
NEXT_PUBLIC_DOCS_BASE_PATH: basePath
2021
},
22+
// MDX screenshots are uncompressed PNGs on GCS with a one-hour cache. ZoomableImage
23+
// routes them through `/_next/image`, which serves WebP from Vercel's edge cache.
24+
images: {
25+
remotePatterns: OPTIMIZED_IMAGE_ORIGINS.map(origin => new URL(`${origin}**`)),
26+
minimumCacheTTL: 60 * 60 * 24 * 7
27+
},
2128
// With basePath set, nothing serves `/`, so the *.vercel.app deployment URL
2229
// that Vercel links from Slack / GitHub 404s. sourcegraph.com never proxies
2330
// `/` to us, so this only affects visitors opening that raw URL. Stay on the

‎src/components/mdx/ZoomableImage.tsx‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,23 @@
22

33
import {useCallback, useEffect, useState} from 'react';
44

5+
import docsConfig from '../../../docs.config';
6+
57
interface ZoomableImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {}
68

7-
export function ZoomableImage({className, alt, ...props}: ZoomableImageProps) {
9+
/**
10+
* Serve allow-listed remote PNG/JPEGs through Next's image optimizer: WebP, at most
11+
* 1920px wide (enough for the content column on a 2x display), cached at the edge.
12+
* The width must be one of Next's default `deviceSizes`.
13+
*/
14+
function optimizedSrc(src: ZoomableImageProps['src']) {
15+
if (typeof src !== 'string' || !/\.(png|jpe?g)$/i.test(src)) return src;
16+
if (!docsConfig.OPTIMIZED_IMAGE_ORIGINS.some(origin => src.startsWith(origin))) return src;
17+
const basePath = process.env.NEXT_PUBLIC_DOCS_BASE_PATH ?? '';
18+
return `${basePath}/_next/image?url=${encodeURIComponent(src)}&w=1920&q=75`;
19+
}
20+
21+
export function ZoomableImage({className, alt, src, ...props}: ZoomableImageProps) {
822
const [isOpen, setIsOpen] = useState(false);
923

1024
const openModal = useCallback(() => setIsOpen(true), []);
@@ -32,6 +46,7 @@ export function ZoomableImage({className, alt, ...props}: ZoomableImageProps) {
3246
<img
3347
className={`cursor-zoom-in rounded-xl ${className ?? ''}`}
3448
alt={alt}
49+
src={optimizedSrc(src)}
3550
loading="lazy"
3651
decoding="async"
3752
onClick={openModal}
@@ -70,6 +85,7 @@ export function ZoomableImage({className, alt, ...props}: ZoomableImageProps) {
7085
<img
7186
className="max-h-[90vh] max-w-[90vw] cursor-zoom-out rounded-lg object-contain"
7287
alt={alt}
88+
src={src}
7389
onClick={e => e.stopPropagation()}
7490
{...props}
7591
/>

0 commit comments

Comments
 (0)