Skip to content

Commit cb47439

Browse files
andrelandgrafkentcdodds
authored andcommitted
image docs & image optimization decision doc
1 parent f7aac86 commit cb47439

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Introduce Image Optimization
2+
3+
Date: 2025-02-19
4+
5+
Status: accepted
6+
7+
## Context
8+
9+
As documented in [018-images.md](./018-images.md), the Epic Stack previously
10+
didn't implement image optimization. Both static app images and dynamic user
11+
images were served as is. However, optimizing images greatly influences web
12+
performance and reduces load times and network load. On the other hand, one of
13+
the guiding principles of the Epic Stack is to limit services (including the
14+
self-managed variety). A great middle ground is to integrate a simple image
15+
optimization solution directly into the web server. This allows each Epic Stack
16+
app to immediately utilize image optimization and serve better web experiences
17+
without prescribing a service.
18+
19+
### Using openimg
20+
21+
The goal of openimg is to be easy to use but also highly configurable, so you
22+
can reconfigure it (or replace it) as your app grows. We can start simple by
23+
introducing a new image optimization endpoint and replace `img` elements with
24+
the `Img` component.
25+
26+
## Decision
27+
28+
Introduce an image optimization endpoint using the
29+
[openimg package](https://github.com/andrelandgraf/openimg). We can then use the
30+
`Img` component to query for optimized images and iterate from there.
31+
32+
In the future, we may decide to look into [unpic-img](https://unpic.pics/img/)
33+
for futher enhancements on the client side and/or look into creating blurred
34+
placeholder images using openimg/node and openimg/vite or alternative solutions.
35+
36+
## Consequences
37+
38+
Serving newly added images will now lead to an image optimization step whenever
39+
a cache miss happens. This increases the image laod time but greatly reduces the
40+
images sizes. On further requests, the load time should also be improved due to
41+
the decreased image sizes.

docs/images.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Images
2+
3+
The Epic Stack distinguishes between user images and static app image assets.
4+
User images are stored in SQLite along with the rest of the application data,
5+
while static images are stored in the [../public/img/](../public/img/) folder.
6+
See the [images decision doc](./decisions/018-images.md) for more details.
7+
8+
## User Images
9+
10+
User images (uploaded by a user) are stored in SQLite and served via resource
11+
routes, for example:
12+
[/resources/user-images/$imageId](../app/routes/resources+/user-images.$imageId.tsx).
13+
14+
## Image Optimization
15+
16+
The Epic Stack uses [openimg](https://github.com/andrelandgraf/openimg) to
17+
optimize images on demand, introduced via
18+
[this decision doc](./decisions/039-image-optimization.md).
19+
20+
### Server Part
21+
22+
The [/resources/images](../app/routes/resources+/images.tsx) endpoint accepts
23+
the search parameters `src`, `w` (width), `h` (height), `format`, and `fit` to
24+
perform image transformations and serve optimized variants. The transformations
25+
are performed with `sharp`, and the optimized images are cached in
26+
`./data/images` on the filesystem and via HTTP caching.
27+
28+
### Client Part
29+
30+
On the client side, the `Img` React component from openimg/react is used to
31+
query the [/resources/images](../app/routes/resources+/images.tsx) endpoint with
32+
the appropriate query parameters, including the source image string. The
33+
component renders a picture element that requests modern formats and sets
34+
attributes such as `fetchpriority`, `loading`, and `decoding` to optimize image
35+
loading. It also computes `srcset` and `sizes` based on the provided `width` and
36+
`height` props. Use the `isAboveFold` prop on the `Img` component to priotize
37+
images that should load immediately.
38+
39+
### Image Sources
40+
41+
If you want to add a new image storage location, like an S3 bucket, update the
42+
[/resources/images](../app/routes/resources+/images.tsx) endpoint and modify the
43+
`getSource` function to instruct openimg on how to retrieve the source images
44+
from the new location. Currently, the endpoint uses fetch requests to retrieve
45+
user images from the resource route endpoints and the filesystem to retrieve
46+
static application assets from the public and assets folders.

0 commit comments

Comments
 (0)