How to Structure Websites in GitHub- A Developer's Guide
Why GitHub Pages Structure Matters More Than You Think
Most developers treat their GitHub repository like a digital junk drawer. Code, docs, images, random config files — everything dumped into one folder. Then they wonder why their GitHub Pages site won't build.
The structure of your repository directly affects:
- Whether GitHub Pages builds successfully
- How fast your site loads
- How easy it is to maintain and update
- Whether Jekyll processes your files correctly
You can have perfect content and broken builds because of a single misplaced file. This guide fixes that.
GitHub Pages: User Sites vs Project Sites
GitHub gives you two types of free hosting:
User Sites
One per account. Your content lives in a repository named username.github.io. The source sits at the root.
URL structure: https://username.github.io
Project Sites
Unlimited per account. Any repository can generate a site via Settings → Pages.
URL structure: https://username.github.io/repository-name
You can also serve a project site from a /docs folder within the repository instead of the root. This keeps your source files separate from your published site.
The Root Directory Structure That Works
For a basic static site (HTML, CSS, JS), your repository should look like this:
username.github.io/
├── index.html
├── about.html
├── contact.html
├── css/
│ └── styles.css
├── js/
│ └── scripts.js
├── images/
│ └── logo.png
└── _config.yml (if using Jekyll)
That's it. No subdirectories deeper than necessary. No random folders.
GitHub Pages serves files from the root by default. Every file you want published needs to be at the root or inside a folder directly under it.
Jekyll Structure: What GitHub Actually Expects
If you're using Jekyll (GitHub's default site generator), the structure changes. GitHub runs Jekyll in safe mode, which means it ignores certain files and folders.
Jekyll Root Layout
username.github.io/
├── _config.yml
├── _includes/
│ ├── header.html
│ └── footer.html
├── _layouts/
│ ├── default.html
│ └── page.html
├── _posts/
│ ├── 2024-01-15-welcome.md
│ └── 2024-02-20-update.md
├── _sass/
│ └── main.scss
├── assets/
│ ├── css/
│ ├── js/
│ └── images/
├── index.html
└── about.md
Files and Folders Jekyll Ignores
Jekyll automatically skips:
- Files and folders starting with
_(except_config.yml) - The
node_modulesfolder - Gemfile and Gemfile.lock
- Files listed in
_config.yml's exclude directive
If your site isn't building, check if you accidentally put content inside a _ folder. Jekyll won't process it.
The /docs Folder Method
Some teams prefer keeping the published site separate from source code. Use the /docs method:
my-project/
├── src/
│ ├── _config.yml
│ ├── index.html
│ └── css/
├── docs/ ← GitHub Pages serves this
│ ├── index.html
│ └── css/
├── .gitignore
└── README.md
Then in your repository settings: Source → Deploy from a branch → /docs folder.
This works well when you have a build process that compiles files into /docs. Your source stays clean.
Common Mistakes That Break Builds
1. Wrong Config File Location
_config.yml must be at the root of your repository or the root of your /docs folder. GitHub won't find it in subdirectories.
2. Case-Sensitive Filenames
Linux servers are case-sensitive. Image.png and image.png are different files. If you reference image.png in your HTML but the file is named Image.png, the image won't load.
3. Broken Relative Links
When your site lives at username.github.io/repo, relative links from index.html work fine. But if you have nested pages, links break.
Use absolute paths for project sites:
<!-- Wrong for project sites -->
<a href="/about.html">About</a>
<!-- Correct for project sites -->
<a href="/repository-name/about.html">About</a>
4. Forgetting the index.html
GitHub Pages requires an index.html or index.md at the root. Without it, you'll get a 404.
Comparing Site Structures
| Method | Best For | Pros | Cons |
|---|---|---|---|
| Root Files | Simple static sites | No build process, fast setup | No templating, manual updates |
| Jekyll | Blogs, documentation | Built-in templating, posts system | Learning curve, Liquid syntax |
| /docs Folder | Project demos, CI/CD workflows | Clean source separation | Extra build step required |
| External CI/CD | Complex sites, multiple environments | Full control, any generator | More setup, potential costs |
Getting Started: Set Up Your Repository
Step 1: Create the Repository
Go to GitHub → New Repository. Name it exactly yourusername.github.io for a user site, or any name for a project site.
Step 2: Choose Your Structure
For a basic site, create this minimum structure:
yourusername.github.io/
├── index.html
├── _config.yml
└── css/
└── style.css
Step 3: Enable GitHub Pages
Repository Settings → Pages → Source → Deploy from a branch → Select main and / (root).
Step 4: Wait and Verify
GitHub takes 30-60 seconds to build. Refresh the Settings page to confirm your site is live. Check for the green banner: "Your site is published at https://..."
If the build fails, go to the Actions tab. GitHub logs every build error there with line numbers.
Asset Organization
Keep your assets predictable:
/assetsor/imagesfor images/cssfor stylesheets/jsfor JavaScript files
Don't scatter assets across multiple folders. It makes debugging broken links a nightmare.
For Jekyll sites, put processed assets in /assets (not starting with _). Jekyll copies everything without an underscore to the final build.
Branch Strategy
You can publish from:
- main branch — direct editing, fastest for simple changes
- gh-pages branch — common for older projects, keeps source separate
- docs folder on main — modern approach for mixed repositories
Stick with main + root for personal sites. Use the docs method when you have source code in the same repository.
What to Exclude from Publishing
Add a .gitignore file to prevent unnecessary files from cluttering your site:
node_modules/
.DS_Store
*.log
.env
_dist/
.cache/
GitHub automatically ignores node_modules for Jekyll sites, but adding it to .gitignore keeps your repository clean.
Custom Domains and Structure
Adding a custom domain doesn't change your structure. GitHub creates a CNAME file at the root when you configure it.
Keep the CNAME file. If you delete it, your custom domain stops working.
Update your DNS to point to GitHub's servers. The structure stays the same whether you use a custom domain or not.