Build + Hosting this Site for Free
by Sean Cha
How I built and hosted this site for free using Svelte 5 + SvelteKit + Github Pages

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 minimaltype checkingenable/disable depending on preferencetailwindcss,sveltekit-adapter,mdsvex, and formatter & linter config of your choicetypographyfor tailwindcss additionstaticfor sveltekit-adapternpmfor package manager
Your selection may differ depending on your needs
Now code away!
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