❓ What is this?

www.cibulka.codes is my portfolio built with Next.js and managed with MDX.

📦  Features

🚧 Next.js 13 (server components)

Next.js 13 is a significant update, introducing the "app directory" with React server components. It packs very powerful features focused on performance and security, but was also relatively unstable for some time with a slight learning curve.

The update allows me to handle a lot of things server-side (image generation, fetching and prefetching data, localization), making things faster and more secure. Like in good old PHP days. 🐘

Certain aspects of the sollution still leaves me somewhat frustrated (the lack of in-built internalization sollution, for instance), but ultimately I'm happy for the switch.


🌐 Internalization (i18n)

A point of frustration with Next.js 13. The previous versions had an in-built solution for providing multiple language versions of your site - it worked out of the box and very well.

Next.js 13, however, listed i18n as a "not planned feature". This prompted the community to find its own sollutions, leading to further instability and breaking updates.

After some months the team finally shared a guide on how you can implement i18n on your own. Unfortunately I still find it quite incomplete.

Here's the list of things I miss from the guide:

Root layout

Majority of Next.js sollutions (including the official ones) expects a "root layout" directly under the app folder that includes at least the <html />. The i18n sollution by Next.js prevents that.

My solution: Keep root layout and provide the locale parametr through custom header.


Custom (and translated) 404 page

One of the solutions requiring the root layout. No custom 404 page for you with the provided guide. This includes localization itself - user of any language will see the error message only in english, not in the language of their choice. 🤷

My solution: Use root layout with custom header as described above.


Unclear SEO for i18n

The provided solution makes the homepage (https://www.cibulka.codes) just a contentless redirect to the subpath with the suitable (negotiated) locale for the user (https://www.cibulka.codes/en).

I'm still trying to figure out how hurtful is this for SEO. Currently I believe that it somewhat is (according to my reports in Google Search Console) and it can be fixed only with additional configuration:

  1. Creating sitemap.xml listing all language versions of your links
  2. Having correct metadata setup of "lang alternates", listing the language versions of the site in the <head />: <link rel="alternate" hreflang={YOUR_LANG} href={YOUR_LANG_URL} />
  3. ... Maybe more things should be done?

Slower TTFB

At least one, probably 2 redirects are happening on the first load.

cibulka.codes > www.cibulka.codes > www.cibulka.codes/en

Due to language negotiation this is hard to cache (if you want to avoid using the cookies as I do). The lag usually stays around 700ms - which is not great not terrible, I suppose.


✍️ MDX administration with Contentlayer

I love Markdown and I am obssesed with MDX! Here's my reasoning:

  1. Even the raw format of your content is easily readable for both humans and machines.
  2. Each piece of the content can be literally a file in a folder. You can query them through filesystem, which is usually quite simple and always very fast.
  3. MDX combines interactive JSX components with standard Markdown. This allows you to create a sort of an API for a content manager - giving them enough flexibility to use the tools you've prepared for them while limiting the complexity.
  4. Due to the simplicity of the format, it is incredibly easy to convert it to anything else you need.
  5. Let's cut out the middleman! There is no need for CMS with this sollution.

A brilliant library Contentlayer turns the MDX files to JSON files during the build step (and creates their TypeScript definitions right away). This works especially well with Next.js 13 and server components, which allows you to leave most of the bulk on the server (and send to the client only the actual content).


📋 PDF export

Creating and maintaining one's CV can be a bit of pain. Duplication is one of the issues: Is your PDF portfolio in sync with your web CV? In all the language versions?

For this reason I've created an automated page generating my CV in print-friendly format. The content comes from MDX files, that are shared between the website, public API and the CV.

The PDF version of the CV is automated by a Puppetteer-powered script. For now I run it manually through NPM script - and whenever I finally learn the GIT actions, this will probably be automated as well. 💪


🪟 Intercepted routes

One of the features of Next.js 13 is an ability to "intercept routes" - the same URL can act differently on direct load or if requested from an app.

This is especially useful for modals: You want to see a content from URL in the temporary dialogue (without resetting the state of the UI under it), but you want to allow the usage of back/forward buttons. Having separate URLs for the important pieces of content is obviously good for SEO as well.

It was a bit tricky to implement for me: Most of the guides expect the existence of the root layout (which is incompatible with the guidelines for i18n, as written above). I also found the syntax a bit confusing. Ultimately I made it work nonetheless. :)


🎥 Custom video player

Nothing special here, just wanted to have a bit more control on how the video is handled (and frankly it was just fun). There are however still some things to polish (allowing the user to change the time, for instance).


🕹️ Tetris

My portfolio also contains my shot at Tetris game. Give it a try, even if it's a bit addictive! 🍭 Or check the repo of the project.

🔧  Install & Use

You know the drill.

npm install
npm run dev # For development
npm run build && npm start # For production preview

PDF is generated with Puppetteer right from http://localhost:3000, so the page must be available on that link during the build time. Whenever it is, run:

npm run cv