Maxim Mikhailov

Web Developer / AI Enthusiast

A screenshot of the TodoMVC app implemented with the framework

mini-framework

A Next.js-like React framework

LinkRepo

This task is a part of the Grit:lab coding school program. In this project our main goal was to create our own frontend framework and use it to implement TodoMVC app.

Why?

For the last few years Next.js became a leader of the frontend frameworks, mostly because of the great developer experience and simplicity. Next.js provides a simple out-of-the-box way to implement React apps with any level of complexity and amazing features like flexible Server-Side-Rendering, image optimization and more. However, I had several concerns about Next.js.

First of all, I like minimalism. More complex systems are always less reliable and harder to control. While Next.js might look minimalistic from a developer point of view (just create a file with a function to define a page), the actual implementation is far from being minimalistic:

Screenshot of the `create-next-app` with React dev tools opened

The screenshot above is a `create-next-app`, the simplest Next.js app. As you can see in the React Dev Tools, even such simple application requires a huge tree of utility components.

Next.js provides too high level of abstraction. The usual Next.js developer is not able to answer what each of these components actually does. I wanted to learn more about how Next.js works under the hood, so I've decided to implement my own version of Next.js.

Features

There're several Next.js features, that we've implemented.

Bundling and dev server. We've used Vite. It's a great build tool that provides the best development experience in the industry. I've used it several times before for my vanilla React projects and was amazed how fast it is comparing to Next.js. Both build time and auto-refresh were multiple times faster, mostly because of the Native ESM based web server.

File-based routing and SSR. In the initial version we've implemented routing using only Vite. There's a great Glob import feature which is extremely easy to integrate with `react-router-dom`.

However, we also wanted to implement a Server-Side-Rendering (pre-rendering) for our app to make it more interactive and reduce the first contentful paint.

After a short research we found Vite-Plugin-SSR, a simple plugin that provides a minimalistic way to implement SSR without loosing control of your application. It handles file-based routing and provides useful naming conventions to control where and how to execute your code.

The only thing we had to do to implement SSR is to create a file `page.server.tsx` and implement a `render` function that will be called on the server side to render our page. This function simply returns an HTML string that will be sent to the client. To render components to string we've used `renderToString` provided by React.

Then on the client side, we should hydrate the SSR rendered code on the client side to make dry string HTML code a wet React component. To do so, we created a `page.client.tsx` file with `render` function that call `hydrateRoot` from React.

The whole setup is just several files with simple TypeScript functions that are easy to read, debug and understand. There's no magic of Next.js anymore, the whole app logic is transparent to developer.

Update: the vite-plugin-ssr project was recently renamed to Vike. Now it's even more developer-friendly than before.

Todo app

Now, when the framework is done, we can implement an app. For state management we've decided to use Redux. We used it because our state should be centralized and persistent due to the nature of the app.

Modern Redux with Redux Tool Kit is much better than it used to be. It was easy to implement all the necessary reducers and persistence middleware to save user's data to `localStorage` and prevent loosing it on page refresh.

For design and styles we've used TailwindCSS with daisyUI.

I really like the result. Our app is truly minimalistic, the React Dev Tools panel is not scary anymore:

A screenshot of the opened dev tools. The component tree is small and easy to understand

And of course the Lighthouse score is really high:

Lighthouse report, all scores are 100 of 100

Conclusion

Next.js is still a leader of the industry, especially after releasing a new app router with React Server Components, which are not currently supported by Vike. However, it was interesting to learn more about what's going on behind the scenes of Next.js. I definitively recommend you checking out Vike and give it a chance.