Mar 11, 2024

How We Built Our 41KB SaaS Website

Fast, beautiful, and fun to build. What we learned, and an open source template you can use to bootstrap your own site.

Over the years, I've built a lot of websites. From FTPing files over dial-up in high school, to web apps with millions of users. I recently returned to web-development after 6-year hiatus. I was not surprised to find there were a ton of new frameworks. When hasn't there been a hip new web-framework everyone had to try? I was surprised to find that some of them addressed almost all my prior pain points.

I decided to take time to learn new tools and design a new “standard stack” for my web development. In the process, I create an open-source template with all the features a SaaS startup needs to get up and running.

In this article, we'll show you how we did it, and share the results.

Topics

Goals

Why create yet another open-source template/boilerplate? With a company, you spend a lot more time working on your startup/web-app than you do on initial setup. The right tools will make you faster and happier. Setting up a stack you love and are productive in will pay dividends in time. My goals:

  • Delightful to work with: should be a great experience for development, dev-ops, and design
  • Productive: The basics like components and themes should be built-in
  • Fast: rendering should be as fast as possible
  • Beautiful: include tools that produce beautiful, professional content
  • Easy to host: I don't want to babysit servers
  • Uncompromised UX: Different pages have different needs. It should support SSR, static pages, CSR and reactivity.

Chosen Tools

Svelte TailwindCSS

After a lot of comparison shopping, I settled on SvelteKit + TailwindCSS as my core stack. Here's why:

Frameworks that compile

Both Svelte and Tailwind are compilers. When you deploy your app, it gets compiled down. It becomes only the HTML, Javascript, and CSS needed for your project. This ensures pages load quickly.

SSR, CSR, or Static pages? Yes!

SSR, CSR, and static site generation each have their pros and cons. In the past, I've had to maintain different stacks for different parts of a project. SSR/static for marketing/blog, and CSR for the web-app.

I was delighted to find that tools like Svelte have addressed this divide. You can write your pages in one framework and separately define how each should be rendered. A page can evolve from static, to highly interactive CSR, and back again; without changing stacks or re-writing pages. This is possible because the same templating framework is used for all rendering modes (reactivity, SSR, CSR, static pre-rendering).

It's even possible to get the best of both worlds (CSR+SSR). The page you first land on is a static page for fast rendering. After the initial render, it asynchronously downloads a bundle with Javascript, HTML, and CSS. This bundle enables instant next-page navigation with CSR.

Productivity + Design Tools

When it comes to productivity, having a UI component library with a built-in theme engine is essential in my book. This means I don't have to waste mental energy deciding what each button or input field should look like. This approach speeds up development. It also keeps design consistent across all your content. After trying out various options, I landed on TailwindCSS + DaisyUI.

TailwindCSS makes developing fast and fun. You no longer need to switch between HTML and CSS files, which is a huge speed boost. I quickly embraced the tailwind way of doing things. I was surprised how much I didn't miss building classes. It's more maintainable to combine related JS, CSS, and HTML into one file, than it is to maintain three separate files.

DaisyUI brings brevity and consistency to Tailwind. It comes with components for all the UI elements most apps need (buttons, sliders, carousels, sliders, etc). Their example nails the productivity/brevity/consistency benefit; in Tailwind, a button requires:

bg-indigo-600 px-4 py-3 text-center text-sm font-semibold inline-block text-white cursor-pointer uppercase transition duration-200 ease-in-out rounded-md hover:bg-indigo-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-600 focus-visible:ring-offset-2 active:scale-95

But with DaisyUI it becomes:

btn btn-primary

The brevity/reuse has the added benefit of decreasing page size.

Hosting

I've run enough services to know that a simple, robust stack pays off. It also lets me sleep well 😊. These are the tactics I used to create a hosting stack which is robust, reliable, and requires minimal monitoring.

Pre-compiling as many pages as possible to static files

  • Once you complete a deployment, few things can break static assets at the app layer. It's up to our host (and their dev-ops team) to keep things running.
  • Maximizes CDN usage, improving end-user performance
  • Reduces costs since our high-volume pages don't need functions/runtime

Serverless when we need server-side execution

  • Auto-scaling means no outages from spikes, and no complex scaling rules.
  • Can run closer to the user (latency)
  • Outages are less likely to happen when a rare set of conditions hits a server. This is because each function is run independently.
  • No long-term server issues (memory leaks, full disks, etc)

Postgres

The one exception to serverless is our database. Postgres is simply awesome. A stateful DB does add monitoring needs, but it pays off in productivity and safety. Postgres gives us strong typing, constraints, relationships, triggers, vector indexes, MVCC, text-search, and more. Going with a hosted Postgres provider moves most monitoring overhead to our host.

For the hosting stack, we chose Cloudflare Pages/Functions, Cloudflare CDN/DNS, Supabase Postgres, and Supabase Auth.

Developer Tooling

Great tools give you confidence. They let you make changes, knowing they will catch issues. We integrated the following tooling (including into the open-source template):

The validation tools run in the editor as you work, in git hooks before you commit, and in continuous integration after you push.

Design Process

Our aim is to create a user experience that combines exceptional performance with a beautiful, functional design.

For visual design, we went with a retro style. It was inspired by 80s electronics packaging (like cassettes and VHS tapes).

Simple Pixel Perfect Rendering

This isn't rocket science. We use simple HTML+CSS for all our rendering. It's fast, reliable, and pixel-perfect. Our best practices include:

  • No background images
  • No videos for backgrounds or animated content
  • Use vector graphics (SVG) over jpg/gif when possible. SVGs give us smaller image sizes and pixel-perfect rendering on any display. They also let images adapt to our theme CSS via currentColor.
  • If raster graphics are needed (rare), use compression and modern formats to minimize their size. The SvelteKit enhanced-img component makes this easy.
  • Inline small images. This prevents extra requests and the page can render in one stage.
  • Add CSS aspect ratios for all non-inline images, to ensure zero content layout shift
  • All pages are designed to be responsive for mobile and desktop
  • Follow accessibility best practices, which our linting helps us find.

Javascript Animations

Fast and small pages don't have to mean a lack of rich content. Our homepage has 5 animations that explain our product. They total over 2 minutes of playback time. For example, we use this animation for our "Increase App Ratings" section:

These aren't videos, these are all built in HTML, CSS, and Javascript. Rendering with web technologies has many benefits:

  • Compared to videos, saves about 99.5% of data (~4MB)
  • Pixel perfect rendering on any display
  • They are interactive, allowing you to progress forward/backward with buttons or swipes
  • Respects our theme system, making style changes easier
  • Simplifies the stack by using the same tech as the rest of our project
  • Responsive to mobile/desktop. For example larger tap targets on mobile
  • Adaptive to scrolling and window position
  • Improved accessibility over videos for screen readers

Some other good examples of HTML/Javascript animations are Antithesis and Superlist.

For our project, everything is hand-coded. We used svelte tooling (animate, motion, transition), CSS animations, and even vanilla CSS. Check out the result at CriticalMoments.io

Not into hand-coding animations? It's not as bad as you think! If you prefer designer-centric workflows check out tools like Rive and Lottie.

Some animations can even be built without Javascript, using only CSS animations:

Performance Results

All the above results in a very fast page. At the time of writing:

Only 41kb to render our homepage

  • Includes 5 animations and all images
  • After gzip/brotli compression
  • Excluding async javascript which is not required to render. That includes framework code for next-page CSR, and analytics.

Renders in under 300ms

  • After clearing the cache, but with warm DNS
  • Sometimes renders in under 100ms!
  • Mileage may vary: depends on your connection speed

Instant next-page rendering

  • After the first page, the next page can load instantly using CSR
  • Mileage may vary: the next-page content starts loading when your cursor hovers over a link. If you are a fast clicker you may see a load

Excellent Page Speed/Lighthouse Scores

The template gets perfect 100 across all categories on desktop. Try it.

Lighthouse scores

Even after adding rich content, it still gets 99 or 100 for performance.

Open Source Template

We've released our template/boilerplate on Github and it's gaining traction. Looks like a lot of folks enjoy the same stack/tooling. We've already received some excellent contributions. Open source FTW!

The boilerplate has many features a new company needs. These include auth, billing, a user portal, settings, a blog engine, a pricing page, a contact us form, and more!

It's 100% Free, MIT Open Source. It includes deployment instructions for a robust and free hosting stack (Cloudflare+Supabase). You can fork it and be online in a few minutes.

View on Github

Conclusion

After my hiatus from web development, I'm impressed by the progress the community has made. The latest frameworks feel like I'm back to writing (almost) pure HTML/CSS/JS, while also delivering all the goodies past frameworks promised (SSR, CSR, reactivity, pre-rendering, compiling, etc). I hope these lessons are useful to others returning to web-dev like me, or just starting out.

If you're starting a project, check out our template. It's a way to quickly bootstrap your next web experience.

Oh, and if you build mobile apps, you should check out the project that inspired all this: Critical Moments!

The Mobile Growth SDK

Critical Moments helps you increase conversions, improve app ratings, and make bugs disappear.