Blog and Portfolio: A Personal Website Built with Next.js

At the beginning of 2023 I learned React, a frontend development library created and maintained by Meta (or Facebook, for friends).
I found React easy to use, and the idea of separating the interface into components made maintenance and scalability much easier.
As an example, we can think of each part of the interface as a piece of Lego which we can use or replace at any time.
At that time I had two objectives in mind:
- Learn Next.js, a React framework.
- Create a portfolio to share my projects and ideas.
I like to use a comparison I read a while ago: let's imagine Iron Man. The person inside the suit is React; Next.js is the suit that gives him superpowers.
As an example, if we want to have routing in React we must install React Router DOM, while in Next.js it is enough to create folders.
Learning with a real project
One of my favorite ways to learn is by creating projects. In this case, I searched for a web portfolio from scratch with Next.js and found one on an English-language YouTube channel.
Every day I spent an hour: I watched a section of the video, tried to replicate it and, if I got stuck or forgot something, I repeated it until I succeeded.
The hardest thing for me was the styles, as I had no experience with Tailwind (the framework used in the video). However, as the days went by, it became easier. I liked that it allows styling with great versatility, unlike other frameworks like Bootstrap that already have predefined components.
Initial technologies and tools
The process lasted about three months and I learned a lot:
- Switch from SVG icons to React Functional Components (RFC).
- Use the Page Router.
- Components such as
LinkorImage, which make it easier to optimize images and navigation. - Bookstore Motion for animations.
- Integration with Tailwind.
- Tools such as Canva to create assets.
In the end, I had a static portfolio, but I wasn't satisfied.
Beyond the Portfolio: The Blog
I wasn't entirely satisfied: I wanted something more personal that reflected part of me and allowed me to give free rein to another hobby which is writing.
I started by creating a contact page using:
- React Hook Form to validate data.
- EmailJS to manage the sending of emails.
- Sonner to show notifications.
- Toast for a light but eye-catching animation next to the form.
Integrating a CMS
It was clear to me that I needed a CMS (Content Management System) to manage content without touching code every time you want to change a project or image.
In short, a CMS rests on three pillars:
- VPS or cloud service to host it.
- Database.
- Warehouse for objects (files, images, etc.).
My options were Outstatic, Tina, Payload, Strapi, and Sanity.
Migrating from Next 13 to 14
Most of these CMSs used the App Router, officially introduced in Next 14. So, to keep my site up to date, I had to migrate from Next 13 to Next 14 and change the Page Router by App Router.
I read the documentation and it seemed as simple as executing a command... And so it was: in less than a minute everything was up to date.
The tricky part was changing the router:
- I created a new folder structure.
- I placed the folder App within SRC to maintain order.
- I learned to work with Server Components, which involved segmenting some components that used animations or hooks as
useState.
Fun fact: It was possible to use both routers at once, which made it easy to progressively migrate.
Implementing Sanity
I chose Sanity because of its free plan and because I wouldn't have to worry about maintaining databases or object containers.
The implementation was quick, although I had a problem with the styles: the CMS panel conflicted with those on my website. I fixed this by wrapping the CMS and app in separate folders.

Automatic Table of Contents
For navigation on the post and project pages, I wanted a table of contents to be automatically generated with the titles of each section.
I based it on the publication How to Make a Table of Contents from Sanity Block Content by Abdurezak Farah, which explains how to extract headings from Sanity and convert them into a hierarchical structure to render them as a listing with internal links.
From that idea, I adapted the code to my project and managed to make sure that every time I create a post or project, the system detects the headings and automatically builds the table of contents.
Reading Time Calculation
To estimate the reading time of each post, I use a very simple formula in my consultation to Sanity, for which I investigated the average number of words a person reads per minute and the average number of letters per word:
const wpm = 180; // Palabras por minuto promedio
const meanWordCharacterCount = 5; // Caracteres promedio por palabra
"estimatedReadingTime": round(
length(pt::text(description)) / meanWordCharacterCount / wpm
)const wpm = 180; // Palabras por minuto promedio
const meanWordCharacterCount = 5; // Caracteres promedio por palabra
"estimatedReadingTime": round(
length(pt::text(description)) / meanWordCharacterCount / wpm
)This line takes the total length of the text (length(pt::text(description))), divides it by the average number of characters per word and then by the words a person reads per minute, then rounds the value to show an integer number of minutes.
Graphic and text resources
For the images I used:
- Adobe Illustrator
- Canva
- LottieFiles
- unDraw
With the exception of Illustrator, all are free tools and highly recommended.
For texts:
- Microsoft Word for drafts.
- Sanity's rich editor to give the final format.
Conclusion
This project not only allowed me to learn Next.js, Tailwind and the integration of a CMS, but also to solve real problems that do not appear in theory.
Today, this portfolio/blog is a window to show some of my projects and share ideas and thoughts, being proof that learning a technology from scratch is possible if it is combined with perseverance and little (a lot actually) patience.
Updates
As of today I have left my site quite neglected, so I will make some changes, I will put aside the idea of having several projects and I will focus on having a few well maintained, within them is this project, the first change I will make is to make it open source and from then on I will continue to implement quarterly updates.
Q2 2026
| Description | Status |
|---|---|
| Upgrade to Next 16 | Implemented |
| Add transition animations for post and project pages | Discarded |
| Create a virtual pet | In process |
| Improve Portable Text integration (tables, footer in images, spacing, and code-type text) | Implemented |
| Add open graph (meta description and image) in posts and projects | Implemented |
| Add social sharing buttons to posts and projects | Implemented |
| Add filter to sort projects and posts by date or name and in descending and ascending | Pending |
For more details you can consult the CHANGELOG of the project.
Comments
All comments
Loading comments...