piech.dev

Back to Projects github.com/tenemo/piech.dev

piech.dev

piech.dev

Netlify Status

My personal page. Over time it turned into a complex project itself:

Not a single word of this readme was written with AI. Letting you know just in case you don't enjoy reading AI-generated documentation.

Projects page of piech.dev

GitHub-based personal projects browser

Pre-rendering React with zero JavaScript served to the user

Lighthouse results

Post-build scripts

As of 2025, if you want to use React for generating static HTML sites and you don't rely on all-batteries-included frameworks/services, the ecosystem support is quite poor. React-router v7 with its framework mode and pre-rendering support changed the situation for the better. Still, it is assumed you will serve the .html static assets and then hydrate the page with JavaScript. If you want to have a true zero-JS output, you have do some of the wiring yourself. Moreover, most popular NPM packages for inlining CSS inline it into each html element's tags, which doesn't support media queries.

This resulted in the project using the following post-build steps:

  1. Transforming image paths to use Netlify Image CDN, scans all .html files and looks for images matching the criteria (it doesn't touch the og:image assets, for instance).
  2. Inlining all CSS. Finds all .html files, resolves the files and dumps all their content into a single <style> tag within <head>.
  3. Removing dist/server, couldn't make Vite + React Router not output it. We are only serving the client "app", which consists of static .html files after build.
  4. Removing dist/client/assets, we don't need CSS/JS assets after CSS has been inlined.
  5. Traversing dist/client to remove all .js files from it. Again, could not make Vite + React Router not output some .js files, even if they were unused and wouldn't be served to the users anyway. Why waste network traffic and time on deploying those?
  6. Generating a sitemap with an in-house script using the sitemap package. vite-plugin-sitemap doesn't work with URLs with dots, apparently, which some of my projects have in their paths, e.g. it truncates /aliases.sh to /aliases.

Technically, I could also flatten the output directory from dist/client to dist, but it's solved with one line of Netlify config and is not bothering me enough to reconfigure all the build/post-build scripts and Netlify deployment configuration.

Why use React at all?

One might ask, if you don't want to bundle any JavaScript, why bother using React at all, just write HTML & CSS or use tools meant for the purpose.

First of all, I want a framework to provide me with a reusable component system and state management logic during the pre-render step and I find React easy to use for this purpose.

There are most likely better libraries/frameworks to do what I'm doing, but the second simple reason why I went with React is that I'm just comfortable with it and I wanted to save time. Did I end up saving time writing all this post-rendering logic and fighting the build tools not to require JS? Probably not, but I embrace the sunk-cost fallacy for my personal site and plan to continue with React :)

Another reason is that I initially intended for the whole page to be hydrated - and I might still start doing that if I need more interactivity - but I realized all that I'm doing can be served without any JavaScript. It's unlikely I'll do it, but it's good to always have that option: with minimal changes to the project, this static-content webpage can be transformed into a client-side React app.

Development

Notes for myself (Piotr) to follow when making changes to the project. At the time of writing it seems super redundant and obvious, but there's a nonzero chance that future Piotr will be very grateful for the below sections.

Adding a new route

Steps to follow when adding a new route to the app:

  1. Write the new route as a feature or sub-feature in src/features/.
  2. Create a new route file in src/routes/ that uses the new component(s) and add at least the og:image tag image asset to public/media/projects/og_images/.
  3. Add the route to src/routes.ts.
  4. Add the route path to the prerender() array in react-router.config.ts
  5. Add <link as="document" href="/.../" rel="prefetch" /> for the new route in src/root.tsx.
  6. Update Header with the new route navigation, if needed.
  7. netlify.toml:
    • If the route replaces any previous route, add a legacy redirect.
    • If there are new media assets for the new route, add appropriate headers.
  8. Add files to sitemap extensions in src/utils/generateSitemap.ts if needed.

Adding a new project

Steps to follow when adding a new project to projects/:

  1. Make sure the GitHub repo is publicly accessible, has topics ("keywords"/"tags"), repo description (the short, character-limited one), and a descriptive README.md, preferably with some media assets to make it more interesting.
  2. Add a video (preferable) or an image to public/media/projects/ that will be project's preview on the project card.
  3. Add an image to be used for og:image and in JSON-LD to public/media/projects/og_images/.
  4. If there are any new technologies used, add them to src/features/Projects/technologies.ts. Add their logos to public/media/logos/.
  5. Add the new project to src/features/Projects/projectsList.ts.

The project should appear in the projects/ route at that point.

At the moment only GitHub-based projects are supported.