How I made this blog
Some notes about the stack I used to build this blog : the hype-driven stack
This article is no longer up to date, as the site has been completely
rewritten using React, Tanstack Start, and TailwindCSS 😅
A few reasons for this change:
- Panda is a great tool, but I wasn't productive enough with it. I decided to switch back to the good old TailwindCSS. But I can definitely see the benefits of Panda.
- SolidJS is also very good, but its ecosystem isn't as mature as React's. For the new version of the blog, I wanted to use React Three Fiber and Motion, so going back to React was the best choice for me here.
- Finally, Vike still felt too unstable. I encountered many breaking changes in 2024, so I decided to just move to Tanstack Start, which is still early too, but seems like a safer bet for the future.
Introduction
Welcome to my website!
It's been a while since I wanted to have a personal site on which to share open source projects I've been working on, but also to share some quick notes or even longer articles.
So building this site was an excellent opportunity to try out some new technologies. That's what I'd like to discuss in this article. Be careful not to take everything too seriously, these are just some of the opinions I formed while building this very, very simple website. I haven't had the opportunity to test these technologies on more serious projects.
Choosing the stack
The goal was to try out a few new "hype" tools, to see what they were worth and form an opinion. The choice was :
- Vike as a meta-framework.
- Solid.js as front-end framework.
- PandaCSS as the styling solution.
- Biome to replace Eslint and prettier for linting and code formatting.
Vike
Why Vike ? Initially, I wanted to try out Astro. It was theoretically the perfect framework for my project. But there is a thing I don't like about Astro: the use of a custom .astro
file format.
This custom format implies the use of a custom LSP for having syntax support, autocompletion and so on. So I installed the Vscode Astro plugin for this purpose. And this custom LSP has a few quirks: in particular, IntelliSense wasn't showing in some cases. And that was immediately a no-go for me. It's probably a small bug in the LSP, but it's symptomatic of the underlying problem of custom LSPs and custom formats : they are not "standards" and therefore not natively supported, often not compatible with existing tools or requiring additional configuration.
So yeah, I'm getting more and more frustrated by that, and that's also what's prompting me to abandon Vue.JS for JSX: more on that later.
So after that, I looked for another framework, and the choice fell on Vike. Vike is a meta-framework similar to Next.JS / Nuxt, obviously less feature-complete, but with the advantage of supporting several front-end frameworks, including Solid.JS. SolidStart was in early-early beta at the time of writing, so I opted for Vike instead.
There's also Vinxi, which seems to be a newly emerging alternative to Vike. It also seems to be very early for the moment, but gonna keep a close eye on it.
Vike is pretty cool, flexible, can do SSG, SSR, full client side, supports several front-end frameworks ... A lot of cool stuff.
Things I didn't like :
-
File-system routing is currently mandatory. I would have liked classic routing. Having files named
+config.h.ts
orposts/@slug/+onBeforePrerenderStart.ts
isn't really my thing. File-system routing has its advantages, but also a lot of disadvantages as soon as the project grows, in my opinion. That said, it's only a matter of time before it's optional, see this issue that talks about it. -
The framework is still, it seems to me, fairly early. A V1 is in in the works. As a result: breaking changes are fairly recurrent, some features are missing, and many many parts of the framework are moving.
-
Related to the previous point: the documentation is generally pretty poor. You shouldn't be afraid to dig straight into the examples and into the Vike source code to understand how certain things work. But that's part of the game with early technologies. All this will improve with time
-
The API, which sometimes seems rather obscure and low-level. But it's all by-design: Vike's philosophy is to "Build your own framework", giving developers a a total freedom. It's a very good choice, but in my use-case: a simple blog, I'd have preferred a more high-level API and a thing that "just works" like Astro. But I am pretty sure we'll see more advanced and specialized starter-kit/framework, based on Vike, in the future.
For example, I had to manually setup the MDX/Blog part of the site, whereas with Astro, it works right out of the box, and with far more functionality than what I built myself with Vike. So yes, I had to reinvent the wheel a bit. But again, it's by-design. Can't wait to see future Vike-based starters and frameworks developed by the community.
What I liked:
- The agnosticism of the front-end framework. You can use Solid.JS, React, Vue ... It's very, very cool. An application will be built in the same way, regardless of the front-end framework, and I like the idea of a standardized format. Where a Nuxt and Next.JS project may have very different concepts and APIs, forcing the developer to learn two different frameworks.
- Flexibility: total control of SSR, renderer, SSG, SPA, routing client side, server side, both... Everything is possible, and quite simply.
- Can easily plug your own HTTP server. Vike can be plugged directly into Adonis, for example. This would enable us to use all Adonis features in our backend/vike logic. So, theoretically, we could have a meta-framework backed by Adonis.
- Powered by Vite.
Solid.JS
For the record, I've pretty much been doing Vue.JS all my life and have avoided JSX-based frameworks ( especially React. ). Recently, I've become increasingly frustrated by the Vue format ( SFC, and its LSP ) which can sometimes be limiting:
A custom file format such as Vue SFC means that all tooling must be customized for this format:
- LSP. Volar. Volar is great, the maintainer is a genius, but day by day I'm getting more and more frustrated by its use. I often find it slow and buggy. Have to restart it every hour or so. Honestly painful.
- Typescript : some features are not supported by SFCs.
- The tools for linting, formatting, typechecking... All this needs to be customized for SFCs. You need a custom parser for ESLint, use
vue-tsc
, activate Volar Take over mode. All this works pretty well, but it's generally a bit complicated to configure and above all: slow. Where with JSX, everything is "native", no custom tooling to configure.
For me, going back to React was a no-go. Although the React ecosystem is fantastic, I find React's reactivity system and paradigm (re-rendering) much less intuitive than Vue's (and signals in general). So, I just decided to try Solid.JS, which seems to be the best of both worlds: signals and JSX.
I know we can use JSX with Vue, but the support is pretty poor, and mostly nobody does it. Try suggesting JSX in a Vue project and you'll see the reactions.
So, I just picked SolidJS and had very almost no surprises with Solid: I quickly found my feet. The paradigm is super similar to Vue, many things are the same. Solid combines the best aspects of Vue and React, without the drawbacks of the latter two. Signals + Best Typescript Support + JSX.
PandaCSS
I've been a long-time user of TailwindCSS. More precisely, of UnoCSS, which is a steroidal alternative to Tailwind. I've rarely had any problems with it, so I've never been too keen to try out an alternative. But after seeing a lot of good words about the Panda, I decided to play around with it a bit. PandaCSS is a CSS-in-JS library with a very light runtime, as everything is generated at build time using static analysis. The library places great emphasis on the following points:
- Type safety. Everything is type safe, whereas with Tailwind nothing is.
- Very light runtime. Everything is generated at build time, unlike conventional CSS-in-JS libraries.
- Full featured: PandaCSS offers a lot of features.
Typesafe CSS ? Why ?
I've long been (and still am, I think) a bit dubious about CSS typesafety. When I develop an application with Uno/Tailwind, here's my flow:
- I modify a component, I add a "bg-rad-400" ( oops, I made a typo not catched by my IDE )
- Constantly, I assume you do the same, I check my browser to see how my component looks now that I've changed my color.
- As a result, I notice that my component isn't red. So I notice and immediately correct my typography.
I don't suppose I'll surprise anyone by saying that I'm constantly checking my browser to see how my component looks after a styling change. That's where I was a bit dubious about the typesafety aspect of CSS. Not sure of the real benefit of it.
That said, it's not the only advantage of having CSS-in-TS and therefore being backed by your LSP. There's also autocompletion, which can come in very handy when you have a lot of custom tokens. Refactoring is also safer when you change the name of a token.
The biggest value I see is in being able to use the types in our design system to interface our props. Code is better than words:
// Instead of manually typing our props
type ButtonProps = {
color: "primary" | "secondary" | "danger";
size: "sm" | "md" | "lg";
};
// We can use the types autogenerated by Panda according to our configuration
import { ColorToken } from "styled-system/tokens";
type ButtonProps = {
color: keyof ColorToken;
};
It's a silly example and not necessarily relevant in the case of Panda, there are better ways of doing this.
Another fear I have with this is the speed of LSP and typechecking. I've worked on projects with a lot of typescript, and sometimes we'd have to wait 2/3 seconds for Intellisense to suggest stuff. That's why, my take is, typesafety is cool, but, let's not abuse it. Let's keep complex types for critical stuff. Otherwise, in 2 months, we'll end up with a super-slow typechecking/autocompletion speed.
To conclude, I'd say that typesafety adds an extra check to build time, which is cool but yeah, still a bit dubious about the real value of it.
Opinions on PandaCSS
Starting with the negative, here are the points I didn't like:
-
The learning curve. I've been working with PandaCSS for a few days now, and I'm still nowhere near as productive and fast as I was with UnoCSS. This learning curve is probably much lower for those used to working with CSS-in-JS libraries, but that wasn't my case: as far as I know, it doesn't exist in Vue (except for pinceau, which is fairly recent. ).
-
API surface. PandaCSS offers a lot of cool features. But one drawback for me is that you sometimes end up with 5 different ways of doing the same thing. And I think, the documentation isn't clear enough about the fundamental differences and the criteria to be taken into account when choosing one or the other.
-
Static analysis. This is both a positive and a negative point. Okay, it allows us to have almost 0 runtime, but sometimes we find ourselves in situations where such and such code doesn't work because static analysis isn't able to understand it. And so we end up going to the Panda playground to make a minimalist reproduction of our problem and try to understand why it doesn't work, and then look for an alternative that does. I suppose it's something you get used to as you use it: you better understand the limitations of static analysis and know which pattern to use to avoid those limitations. But it cost me a few hours on this project.
That said, static analysis is sometimes bluffing, and you sometimes forget that it's static, as Alex raises it here. Note that there's also an escape-hatch with the
staticCss
option to generate styles even if not found by static analysis. -
Feeling of over-engineering. Here I've just built a simple, dumb blog. And I feel like I've used a totally overkill solution for my needs. In fact, PandaCSS is probably not the right tool for small-scale projects. Probably more something that should only use when you have complex design systems to create.
Now to the positives:
Overall, it was a cool experience. The tool is very powerful compared to a simple library of utility classes. Lots of cool discoveries for me, as I've never used a CSS-in-JS library before.
-
Recipes. With UnoCSS, when I need to re-use a styling set, then I make a new Vue/React Component. Under Panda, if the same component doesn't have its own logic, you can just define a recipe, which is much simpler and faster.
-
Patterns and JSX. Panda ships JSX components out-of-the-box. So we have components like
<Flex>
<Center>
<HStack>
<VStack>
<Grid>
that we can use directly. We can pass our css properties directly to these components. It's pretty cool and makes the code elegant.
import { Box, Stack, Circle } from "./styled-system/jsx";
function App() {
return (
<Stack direction="row" p="4" rounded="md" shadow="lg" bg="white">
<Circle size="5rem" overflow="hidden">
<img src="https://via.placeholder.com/150" alt="avatar" />
</Circle>
<Box mt="4" fontSize="xl" fontWeight="semibold">
John Doe
</Box>
<Box mt="2" fontSize="sm" color="gray.600">
john@doe.com
</Box>
</Stack>
);
}
- Full-featured. I'm not going to list all Panda's features. But there are many: Semantic tokens, variants, cva, slot recipes, responsive design... Lots of good stuff that I won't be able to detail here.
So yeah, to conclude, PandaCSS is a very good tool, very well thought out, a bit like the advent of CSS-in-JS. I'm going to keep using it to see if I regain my productivity and if this impression of over-engineering finally disappears with time and habit.
Biome
I've spent a lot of time setting up Eslint custom presets, especially for the teams I have to work with, because I hate making nitpicking comments on PRs. I would rather have everything automated than waste time making this kind of comment.
However, ESLint can sometimes be very slow on large codebases. I'm not a big fan of speed for speed's sake, but sometimes yeah, it's a bit annoying
So, out of curiosity, I wanted to try Biome, which is a tool designed to replace Eslint and Prettier for linting and formatting code. It's pretty cool, it's really super fast (~15x faster than Eslint ), configuration is super simple ( no dozens of plugins to install ).
But then comes the biggest drawback for me: interoperability with the ESLint ecosystem. For example, I love eslint-plugin-perfectionnist
for sorting my imports, or eslint-plugin-unicorn
for certain code quality rules, and typescript-eslint
for linting typescript. Well, these plugins aren't compatible with Biome and they don't have an alternative. For me, this is Biome's biggest problem: you lose a lot of interest if you have 90% of the rules you used to use that are no longer available.
Also, Biome formatter works great and is super fast: at the time of writing, Biome's formatter only works with JS/TS and Jsonc. HTML, CSS, Vue... are not yet supported, but are planned. So kind of not usable in another project yet for me.
In conclusion, this is a promising tool, very fast and one to keep a close eye on. The team plans to improve on the points I raised: support new languages for the formatter, add a system enabling types linting, a plugin system that will enable the community to port ESLint rules.
That said, porting the ESLint ecosystem to Biome and re-developing an alternative to typescript-eslint
is, it seems to me, a huge work. So stay tuned!
There's also oxc
, another tool that aims to replace ESLint. ( In fact, OXC
is more than a "replacement for eslint". But let's keep that aside ). I
haven't had a chance to try it, but it also looks promising. The tool has
ported quite a few of ESLint's popular plugin
rules
Conclusion
In the end, after a bit of trouble configuring the whole thing, the experience was relatively pleasant. However, all those configuration and gluing problems confirmed once again why I love AdonisJS, was a good reminder. But having said that, there are a few tools I'll continue to use in the future, and I'm looking forward to seeing how they evolve