Home — sean cha
LinkedIn

Build + Hosting this Site for Free

by Sean Cha

How I built and hosted this site for free using Svelte 5 + SvelteKit + Github Pages

svelte machine cover photo

Disclaimer

This isn’t a comprehensive step-by-step tutorial on how to fully replicate my site—at least not yet. Rather, this is to show and explain the tech stack with the side-benefit of showing that I didn’t vibe-code absolutely all of it ;)

Motivation

As an engineer who is yet another cog in the machine at the mercy of employeers and recruiters, I wanted to make a website that served as my personal portfolio—something more visual and engaging than my resume pdf.

But I didn’t know anything about frontend and web design (still don’t, really), so my best solution was to use a Squarespace website.

The problem is that this was $25/month for building the website drag-and-drop style and hosting it.
Easy but more than I was willing to pay.

So I bit the bullet and learned the minimum amount of HTML and CSS that I needed to make a basic portfolio website that I could host using Github Pages.
But it looked a little too basic and I had trouble making everything mobile-friendly, which I know is more of a skill issue than a framework issue, but still, I wanted something better.

So theeeen I looked to javascript/web-dev frameworks like React, Vue, Angular, Svelte…etc, and I tried React and Svelte tutorials and decided that Svelte seemed a bit more intuitive to me. This website is the result of that decision.

Web-dev Frameworks and Tools Used

Wah eh” All This?

I will only give a very brief description of each of these things since their respective docs pages do a better job at the more detailed explanations.

Svelte

UI Framework that lets you create “reactive” components using CSS, and a superset of HTML and JavaScript

Reactive basically referring to when UI components update themselves automatically when any of their dependent data gets updated

What do I mean by ‘superset of HTML and Javascript’? Well, the HTML and JS that you write in a Svelte project has additional Svelte-specific features that is made possible by the fact that Svelte is a compiler.

The code that you write gets compiled into vanilla/plain JS by the Svelte compiler to be directly run in the browser. Therefore, while something like React uses a Virtual DOM engine in order to enable fast reactivity, Svelte embeds this reactivity into the Javascript that it compiles so that it doesn’t have to bundle reactivity libraries with your code in order for it to work on the browser.

It also means that Svelte can basically add any arbitrary syntax rules (whether for good or bad) to its code.

For example, you can have logic blocks in your HTML directly:

{#if condition}
{:else if other}
{:else}
{/if}

…and two-way reactivity:

<script>
  let name = $state('');
</script>

<input bind:value={name}>

…and a lot more other things that you can read about here

SvelteKit

Svelte is only in charge of rendering UI compopnents. SvelteKit has all the extra tools you need on top of Svelte to build a full app.

In the case of this website, this includes:

  • Project management and dev tools (SvelteKit wraps around Vite)
  • Routing
  • Client, Server Side, and Static Rendering configuration (see here)

Read more about it here.

Github Pages

Github’s own page explains it best:

GitHub Pages is a static site hosting service that takes HTML, CSS, and JavaScript files straight from a repository on GitHub, optionally runs the files through a build process, and publishes a website.

Github Pages hosting is free, but it supports static pages only, meaning that the server needs to be able to render your page at build-time before it reaches your user, with optionally having the client (not the server) finish the rendering and hydration upon client request using the javascript that the server sends

NOTE: This is not to be confused with server-side rendering, which can newly render UI as the client requests it. This is pre-rendering, where the UI is already rendered BEFORE the client requests it.

In basic terms, this means custom server functions are not allowed, e.g. Node.js/Python/Ruby/PHP server, database queries, API routes that run on the server, and server-side rendering.

In SvelteKit, this corresponds to using their static rendering adapter, adapter-static.

Tailwind CSS

Tailwind CSS is a popular CSS toolkit that comes with many combinable built-in utility classes that removes the need for a lot of CSS that you’d need to otherwise write yourself.

It is also what is used by Shadcn (see next section)

Shadcn-Svelte

Shadcn is a web UI component library (they say it’s not a component library, and rather—I guess—a component code library but whatever) that is open-source, open-code.

Shadcn-Svelte is an official-ish port of Shadcn specifically made for Svelte.

mdsvex

From Svelte themselves:

mdsvex is a markdown preprocessor for Svelte components - basically MDX for Svelte. It allows you to use Svelte components in your markdown, or markdown in your Svelte components.

See mdsvex’s site

Project Setup

First, you need Node. I use Node Version Manager (NVM)

To use the same exact node version as me:
nvm install 25.2.1
nvm use 25.2.1

or just:
nvm install node
nvm use node

You can create a Svelte project using SvelteKit or Vite by itself. I chose SvelteKit:
npx sv create <project-name>

When prompted, select:

  • SvelteKit minimal
  • type checking enable/disable depending on preference
  • tailwindcss, sveltekit-adapter, mdsvex, and formatter & linter config of your choice
  • typography for tailwindcss addition
  • static for sveltekit-adapter
  • npm for package manager

Your selection may differ depending on your needs

Now code away!

Svelte tutorial :D

You can see a live preview of your website by running:
npm install
npm run dev

Compile and build by running:
npm run build

To preview the build:
npm run preview

Hosting the Site

Now, get your website on the internet!

To use Github Pages, you must enable it in the repo settings

By default, each Github user and organization page is allocated:
http(s)://<owner>.github.io

And for each repo:
http(s)://<owner>.github.io/<repositoryname>

There are two ways for Github to find your website:
You can target a branch to build and optionally a folder within that branch.
Or you can use Github Actions to define custom build steps.

In my case, I used the second option, with my .github/workflows/deploy.yml looking like:

name: Deploy to GitHub Pages

on:
  push:
    branches: ['main']

jobs:
  build_site:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Install Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 25
          cache: npm

      - name: Install dependencies
        run: npm i

      - name: build
        env:
          BASE_PATH: '/${{ github.event.repository.name }}'
        run: npm run build

      - name: Upload Artifacts
        uses: actions/upload-pages-artifact@v3
        with:
          path: 'build/'

  deploy:
    needs: build_site
    runs-on: ubuntu-latest

    permissions:
      pages: write
      id-token: write

    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}

    steps:
      - name: Deploy
        id: deployment
        uses: actions/deploy-pages@v4

This way, Github will be able to run npm install and npm build for me and deploy from what is inside the build folder.

Now, if you want to use a custom domain name like I did, buy a domain from a provider (Cloudfare, Namecheap, Squarespace, etc) and then configure it on Github

And finally, I present: seancha.com