How to create website thumbnails for a GitHub Pages project directory
Add a useful image to every GitHub Pages project card. Learn where to store thumbnails, how to link them on project sites, and how to fix broken paths.
To add thumbnails to a GitHub Pages project directory, put an image for each project in the site’s configured publishing source, add its path to the project entry, and render it in a linked card. For a project site hosted under /, make sure the image URL includes that base path; a root-relative URL such as /assets/thumbnails/project-one.jpg may point to the wrong location.
A thumbnail is part of your published page, separate from the repository’s social preview image. GitHub Pages publishes static files from a repository and can also build a site, for example with Jekyll. Published files retain the directory structure of the publishing source. GitHub Pages overview · Creating a GitHub Pages site.
1. Put thumbnail files in the publishing source
Choose a representative image for each project. A browser screenshot can work well, but any image that accurately previews the project is suitable. Add the image files to the directory GitHub Pages publishes. For example:
project-directory/
index.html
assets/
thumbnails/
project-one.jpg
project-two.png
projects/
project-one/
index.html
css/
style.css
This is an example organization, not a required GitHub layout. If your publishing source is a folder such as docs/, place the thumbnails under that folder so they are included in the published output. Keep filenames simple and consistent, and use a format and image dimensions appropriate for your card design.
2. Render a linked project card in plain HTML
For a hand-written static site, place the image inside the link to the project. Include alternative text that describes the image’s useful content; it should still make sense to someone who cannot see it.
<a class="project-card" href="projects/project-one/">
<img
src="assets/thumbnails/project-one.jpg"
alt="Screenshot of Project One's dashboard"
loading="lazy"
width="640"
height="400"
>
<h2>Project One</h2>
</a>
The relative image URL is resolved from the page URL. If the directory page is at the site root, assets/thumbnails/project-one.jpg points into its assets directory. If the same markup is placed in a nested page, adjust the relative path accordingly. The width and height attributes reserve space while the image loads; change them to match your chosen aspect ratio. loading="lazy" can defer off-screen images on a long listing.
Minimal card styling
.project-card {
display: block;
overflow: hidden;
border: 1px solid #d8dee4;
border-radius: 0.75rem;
color: inherit;
text-decoration: none;
}
.project-card img {
display: block;
width: 100%;
aspect-ratio: 8 / 5;
object-fit: cover;
}
.project-card h2 {
margin: 0;
padding: 1rem;
}
object-fit: cover fills the thumbnail area by cropping excess edges. Use object-fit: contain if showing the entire image matters more than filling the card.
3. Use project data if the directory has many entries
For a small list, writing each card in HTML is straightforward. If projects are maintained as data or generated by a build, keep each project’s title, destination, thumbnail path, and alt text together. The following plain JavaScript example builds cards from an array:
<div id="projects" class="project-grid"></div>
<script>
const projects = [
{
title: "Project One",
href: "projects/project-one/",
image: "assets/thumbnails/project-one.jpg",
alt: "Screenshot of Project One's dashboard"
},
{
title: "Project Two",
href: "projects/project-two/",
image: "assets/thumbnails/project-two.png",
alt: "Map view from Project Two"
}
];
const grid = document.querySelector("#projects");
for (const project of projects) {
const link = document.createElement("a");
link.className = "project-card";
link.href = project.href;
const image = document.createElement("img");
image.src = project.image;
image.alt = project.alt;
image.loading = "lazy";
image.width = 640;
image.height = 400;
const title = document.createElement("h2");
title.textContent = project.title;
link.append(image, title);
grid.append(link);
}
</script>
For a Jekyll site, the equivalent data may live in a page, layout, or data file, depending on the site’s structure. Jekyll pages can use front matter and layouts, and GitHub documents local preview and deployment guidance. Add content with Jekyll.
4. Make asset paths work on project sites
A user or organization site is generally served at the host root, while a project site is served below its repository name. That difference affects URLs. An absolute path beginning with / starts at the domain root, so /assets/thumbnails/project-one.jpg can miss the repository subpath on a project site.
Plain HTML: use paths relative to the page
When the listing page and assets are arranged as in the example, a relative URL avoids hard-coding the domain or repository name:
<img src="assets/thumbnails/project-one.jpg" alt="Screenshot of Project One's dashboard">
For a page nested one directory below the site root, use a path such as ../assets/thumbnails/project-one.jpg, or define a consistent base path in your site’s own code. Verify the resolved URL in the browser because page depth changes relative URL resolution.
Jekyll: configure and apply baseurl
For a Jekyll project site, set the repository subpath in baseurl in _config.yml, then use the relative_url filter for generated asset links:
# _config.yml
baseurl: "/repository-name"
<img
src="{{ '/assets/thumbnails/project-one.jpg' | relative_url }}"
alt="Screenshot of Project One's dashboard"
>
Use the base path appropriate to the actual publishing configuration; a user or organization site at the domain root generally does not use a repository-name subpath. Confirm that your theme or build supports the filter and that the generated URL matches the published site. GitHub’s Jekyll documentation explains configuring baseurl for a site served from a subdirectory. GitHub Pages project sites and base URLs.
5. Create and maintain useful thumbnails
- Choose an image that communicates what the project does at card size.
- Use descriptive alt text, such as “Screenshot of the project’s task board,” rather than a filename or generic “image.” GitHub describes alt text as a short text equivalent for image information. GitHub documentation on images and alt text.
- Keep the crop and aspect ratio consistent across cards if the grid should align.
- Compress large source images to keep the page responsive. Retain enough detail to recognize the project in the displayed card.
- Update the thumbnail when the project’s interface or purpose changes, and check that its path still matches the project entry.
GitHub’s recommended image dimensions for a repository social preview are specifically for that separate feature; they are not a required size for thumbnails inside a Pages website. The repository social preview is configured separately in repository settings. GitHub’s social preview guidance.
6. Preview and publish
- Confirm that the HTML, styles, and image files are inside the configured publishing source.
- Preview locally. For a Jekyll site, use the local preview workflow documented by GitHub and check the generated asset URLs.
- Publish using the repository’s configured Pages source or workflow. GitHub recommends GitHub Actions for deployment in its current guidance.
- Open the published project URL, navigate to the directory page, and inspect each card. Check the browser’s network panel or open an image URL directly if one is missing.
- Test at least one project-site URL under the repository path, since a page that works at a local root may still have an incorrect production base path.
Or skip the browser setup
You can create a project thumbnail from a page with ScreenshotNeo’s website screenshot API: one GET request returns an image or PDF. The service removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, failed loads, and cache hits are not billed. Its MCP server lets AI agents use screenshot tools, and 1,000 screenshots per month are free with no card; paid plans start at $5 for 3,000. See the ScreenshotNeo website and API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
Replace the example URL with the project page you want to preview, and save the returned image in the site’s publishing source. The API supports PNG, JPEG, and WebP output; consult the ScreenshotNeo documentation for request options. Sign up for 1,000 free screenshots a month, with no card required.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Thumbnail is broken only on the published project site | A root-relative path omitted the repository base path. | Use a page-relative path or generate the URL with Jekyll’s configured baseurl and relative_url; inspect the final image URL. |
| Image works on the directory page but not a nested page | The relative path is resolved from the nested page’s URL. | Adjust the number of ../ segments or use the site generator’s base URL helper. |
| Image is missing everywhere | The file may not be in the publishing source, its name may differ in case, or the URL may be misspelled. | Check the committed path and capitalization, then open the exact generated image URL. |
| Jekyll outputs a URL without the repository prefix | baseurl is unset or incorrect, or the path was not passed through relative_url. |
Set the repository subpath in _config.yml and regenerate the page using the supported filter. |
| Cards jump while images load | The browser does not know the image’s display dimensions in advance. | Set accurate width and height attributes or reserve space with CSS aspect-ratio. |
| Thumbnail crops off important content | object-fit: cover crops the edges to fill the card. |
Use contain, change the crop, or choose a more suitable source image. |
| Local preview looks right but the deployed site does not | The local server may serve at a different base path, or the deployed publishing source may differ. | Check the configured Pages source and test the deployed URL, including its repository subpath. |
Performance, reliability, and cost
For a static directory, thumbnail files are served as part of the published site; the main maintenance work is keeping paths and project entries accurate. Compress images to reduce transfer size, reserve display dimensions to limit layout movement, and lazy-load cards below the initial viewport when the listing is long. A failed or removed asset produces a broken preview, so periodically inspect the published directory after changing filenames or the publishing source.
The do-it-yourself approach has no screenshot API charge: you create or capture the images, commit them, and maintain them with the site. If you automate capture, account for the time and infrastructure needed to run a browser and the storage and publishing of resulting files. ScreenshotNeo is an API option when you want a capture request instead of setting up browser automation; its published plans include a free tier and paid tiers listed on its site. Use current plan details there before choosing a tier.
FAQ
Should I put thumbnails in the repository README?
Not for the in-page project cards. The cards need image files referenced by the published website. A README image and the repository’s social preview serve different contexts.
Does GitHub require a specific thumbnail size?
The supplied GitHub guidance gives dimensions for repository social previews, not for images within a Pages site. Choose dimensions that fit your card design.
Can I use a screenshot of a page that needs sign-in?
Only if you can capture and publish it appropriately. Do not include private account data, secrets, or information that should not be public in an image committed to a public site.
Do project cards need JavaScript?
No. Plain HTML links and images are enough. Use JavaScript or a site generator only if it helps maintain a larger project list.


