How to View Static Websites: The Three Loops, the Four Toolchains, and the Part the Top Guides Skip

Sean

Platform Writer

Jun 11, 2026
4 min read

Viewing a static website is opening a browser and going to a URL — but the URL matters, and the loop the developer is in matters more than the URL. The three loops are: the local file loop (file:///path/to/index.html), the local server loop (http://localhost:8000), and the deploy preview loop (https://pr-123.example.com). Each loop has a different audience, a different network setup, a different way to debug, and a different reason it exists. The interesting part is the four toolchains (raw HTML, framework dev server, static site generator, headless CMS), the way each toolchain makes the developer open the browser a different way, and the part the top guides skip — the live preview loop, the cross-device check, and the way to read the page like a customer rather than like a developer.

The reason “how to view static websites” is its own question and not just “how to view a website” is that the static website has a particular affordance: the file is on disk, the file can be opened directly, and the file is the same in development and in production. The affordance is the reason the developer’s workflow is different from the dynamic website’s workflow, and the affordance is the lever that turns “I cannot see my changes” into “I can see my changes immediately.”

How to view static websites: the three loops, the four toolchains, and the part the top guides skip

Table of contents

The short version

The three loops are: open the HTML file directly in the browser, run a local server (python3 -m http.server 8000 or npx serve), and deploy to a preview environment (Vercel, Netlify, Cloudflare Pages, Render Static Sites). The four toolchains are: raw HTML (no build), a framework dev server (Astro, Next.js, SvelteKit, Hugo, Eleventy), a CMS-driven workflow (Sanity, Contentful, Strapi, Decap, Tina), and a hosted IDE (CodeSandbox, StackBlitz, Replit, GitHub Codespaces). The right loop depends on the stage of the project, the audience for the preview, and the toolchain the developer is using.

The three loops, in order of how often a developer uses them

A short, opinionated list of the three loops a developer uses, in the order the developer uses them. The three cover 99% of real work, and the three are the ones a developer should know.

The local file loop (file:///path/to/index.html). The developer opens the HTML file directly in the browser, and the browser renders the file from disk. The loop is the right answer for a developer who is working on a single-file static site, a developer who is making a small change, or a developer who is debugging a CSS issue that does not need a server. The loop is the fastest loop, the loop is the one a developer uses most often, and the loop is the one that breaks most often (because the file is served from file://, which has different security and CORS rules than http://).

The local server loop (http://localhost:8000). The developer runs a local server (python3 -m http.server 8000 for any directory, npx serve for a Node.js project, bundle exec jekyll serve for a Jekyll project, hugo server for a Hugo project), and the browser renders the file from the local server. The loop is the right answer for a developer who is working on a multi-page static site, a developer who is using a framework, a developer who is testing CORS, a developer who is using fetch to load JSON, or a developer who is using a service worker. The loop is the one a developer uses second most often, the loop is the one that requires a small bit of setup, and the loop is the one that catches 90% of “works on file://, breaks on http://” bugs.

The deploy preview loop (https://pr-123.example.com). The developer pushes a branch, the platform builds the static site and deploys it to a preview URL, the browser renders the file from the platform’s CDN. The loop is the right answer for a developer who wants to share the site with a teammate, a client, a stakeholder, a mobile device, or a real customer. The loop is the one a developer uses least often (because it requires a build and a deploy), the loop is the one that catches the bugs the local server cannot (CDN caching, real DNS, real TLS, real latency), and the loop is the one a developer should be using more often.

The three loops are the floor. There is also the ngrok / cloudflared / localtunnel loop (the developer exposes the local server to the public internet for a webhook test, a real-device test, or a stakeholder demo), the git worktree loop (the developer works on multiple branches in parallel and views each in a different local port), and the Docker loop (the developer runs the static site in a container, which is rare but useful for consistency). The three are the ones a developer should know first, and the three are the ones a developer can rely on.

The four toolchains and how each one opens the browser

A short, opinionated list of the four toolchains a developer uses, and how each one opens the browser. The four cover 99% of real static site work, and the four are the ones a developer should know.

Raw HTML (no build step). A directory of HTML, CSS, JavaScript, and asset files. The developer opens the file directly (file:///path/to/index.html) or runs a simple local server (python3 -m http.server 8000). The pattern is the right answer for a single-page site, a small marketing page, a documentation page, a quick prototype, and the pattern is the one a developer uses when the developer does not want a build step.

Framework dev server (Astro, Next.js, SvelteKit, Hugo, Eleventy, Gatsby, Nuxt, VuePress, VitePress). A framework that compiles the source files (Markdown, MDX, Vue, Svelte, JSX, TSX) into static HTML, CSS, and JavaScript. The developer runs the framework’s dev server (npm run dev, npm start, hugo server, gatsby develop) and the browser opens http://localhost:3000 (or http://localhost:5173 for Vite, or http://localhost:1313 for Hugo). The pattern is the right answer for any project that has more than 5 pages or that needs a build step, and the pattern is the one a developer uses most often.

Headless CMS or git-based CMS (Sanity, Contentful, Strapi, Decap, Tina, Storyblok, Builder.io). A CMS that stores the content (blog posts, docs, marketing pages) separately from the code. The CMS provides a local preview server (sanity dev, contentful-cli, tina dev) or a hosted preview environment, and the developer opens the preview URL to see the rendered site. The pattern is the right answer for a project where the content is owned by a non-developer (a marketing team, a docs team, a content team), and the pattern is the one a developer uses when the developer wants the content team to be able to edit the site without touching the code.

Hosted IDE (CodeSandbox, StackBlitz, Replit, GitHub Codespaces, Gitpod). A cloud-based development environment that runs the build, the dev server, and the preview in the browser. The developer opens the IDE in the browser, the IDE runs the dev server, and the IDE shows a preview pane. The pattern is the right answer for a quick prototype, a learning project, a code review, a pair programming session, and the pattern is the one a developer uses when the developer does not want to set up a local environment.

The four toolchains are the floor. There is also the “static + edge functions” toolchain (Cloudflare Pages, Vercel, Netlify with serverless functions for the dynamic parts), the “static + JAM” toolchain (Astro, SvelteKit, Next.js with a headless CMS), and the “static + server” toolchain (a static site generated by a server-side language like PHP, Ruby, or Python). The four are the ones a developer should know first, and the four are the ones a developer can rely on.

The way to read the page like a customer

A short, opinionated list of the four ways to read the page like a customer rather than like a developer. The four are the parts the top guides skip, and the four are the ones that turn “I built it correctly” into “the customer thinks it works.”

Open in an incognito window. The developer is logged into their CMS, their analytics, their ad networks, their dev tools. The customer’s browser is not. The fix is to open the page in an incognito window (Cmd+Shift+N in Chrome, Cmd+Shift+P in Firefox) and read the page as a customer would.

Read the page on a phone. The developer is reading the page on a 27-inch monitor. The customer is reading the page on a 6.1-inch phone. The fix is to open the page on a real phone (or in the browser’s device emulation mode), and the fix is the lever that turns “the layout looks great in my monitor” into “the layout looks great in the customer’s hand.”

Read the page on a slow connection. The developer is on a 1 Gbps fiber connection. The customer is on a 4G connection in a coffee shop. The fix is to open the page in the browser’s network throttling mode (Chrome DevTools → Network → “Slow 3G” or “Fast 3G”), and the fix is the lever that turns “the page loads in 200ms for me” into “the page loads in 2s for the customer.”

Read the page with the JavaScript disabled. The customer might be using a browser with JavaScript disabled (a privacy browser, a screen reader, a corporate browser with strict security policies). The fix is to disable JavaScript in the browser (chrome://settings/content/javascript or a browser extension like NoScript) and read the page, and the fix is the lever that turns “the page requires JavaScript” into “the page works without JavaScript.”

The four are the parts the top guides skip, and the four are the ones that catch the bugs the developer’s own browser cannot. A developer who reads the page in the four ways is a developer who is going to ship a site the customer is happy with.

The cross-device check the top guides skip

A short, opinionated list of the three cross-device checks the top guides skip. The three are the ones a developer should do before they ship, and the three are the ones that catch the bugs that show up on a specific device or browser.

The real-device check. The developer is testing in Chrome on a Mac. The customer is using Safari on an iPhone. The fix is to test on a real iPhone, a real Android, a real iPad, a real Windows machine, and a real Linux machine, and the fix is the lever that turns “the layout works in my browser” into “the layout works on every device.”

The cross-browser check. The developer is testing in Chrome. The customer is using Safari, Firefox, Edge, Samsung Internet, Brave, Arc, or a dozen other browsers. The fix is to test in at least Chrome, Firefox, Safari, and Edge, and the fix is the lever that turns “the layout works in my browser” into “the layout works in every browser.”

The accessibility check. The developer is testing with a mouse and a 27-inch monitor. The customer is using a screen reader, a keyboard, a switch device, a high-contrast mode, or a screen magnifier. The fix is to test with a screen reader (NVDA on Windows, VoiceOver on macOS/iOS, TalkBack on Android), a keyboard-only navigation, and a high-contrast mode, and the fix is the lever that turns “the layout works for me” into “the layout works for everyone.”

The three checks are the parts the top guides skip, and the three are the ones that catch the bugs that show up on a specific device, browser, or assistive technology. A developer who does the three checks is a developer who is going to ship a site that works for everyone.

The seven gotchas that quietly break the local preview

A short, opinionated list of gotchas that have actually broken real local previews. None of them are dramatic. They are the boring ones.

The CORS error on file://. A developer who opens index.html directly in the browser and then uses fetch to load a JSON file is a developer whose fetch is going to fail with a CORS error (browsers block fetch on file:// for security reasons). The fix is to run a local server (python3 -m http.server 8000), and the fix is the lever that turns “the JSON loads on http:// but not on file://” into “the JSON loads on both.”

The service worker from a previous project is still registered. A developer who worked on a different project that registered a service worker is a developer whose new project is going to serve cached assets from the old project. The fix is to unregister the service worker (DevTools → Application → Service Workers → Unregister), and the fix is the lever that turns “the new project shows the old project’s content” into “the new project shows the new project’s content.”

The browser cache shows the old version. A developer who refreshes the page with Cmd+R is a developer whose browser is going to serve the cached version. The fix is to hard refresh (Cmd+Shift+R or Ctrl+Shift+R), and the fix is the lever that turns “the change is not showing” into “the change is showing.”

The local server is running on the wrong port. A developer who runs python3 -m http.server 8000 and then opens http://localhost:3000 in the browser is a developer whose browser is going to show “connection refused.” The fix is to check the port the server is actually running on (the output of the python3 -m http.server command), and the fix is the lever that turns “the connection is refused” into “the connection is working.”

The node_modules is not installed. A developer who clones a project, opens it in their editor, and tries to run npm run dev without running npm install first is a developer whose npm run dev is going to fail. The fix is to run npm install (or pnpm install, yarn install, bun install) first, and the fix is the lever that turns “the dev server fails to start” into “the dev server starts.”

The node_modules is from a different Node version. A developer who clones a project built with Node 20 and runs npm install with Node 16 is a developer whose node_modules is going to be incompatible. The fix is to use the right Node version (with nvm, fnm, n, or volta), and the fix is the lever that turns “the dev server crashes” into “the dev server starts.”

The framework’s hot reload is stuck. A developer whose framework’s hot reload is stuck on an old version of the file is a developer whose changes are not showing in the browser. The fix is to restart the dev server (Ctrl+C and npm run dev), and the fix is the lever that turns “the hot reload is broken” into “the hot reload is working.”

The way to share a preview with a teammate or a client

A short, opinionated list of the three ways to share a preview. The three are the ones a developer will use most often, and the three are the ones a developer should know.

The ngrok / cloudflared / localtunnel loop. A developer runs the local server and exposes it to the public internet through a tunnel (ngrok http 8000, cloudflared tunnel --url http://localhost:8000, npx localtunnel --port 8000). The tunnel gives the developer a public URL (https://abc123.ngrok.io) that the developer can share with a teammate, a client, or a stakeholder. The pattern is the right answer for a quick demo, a webhook test, a real-device test, and the pattern is the lever that turns “I can show this on my machine” into “I can show this on a public URL.”

The deploy preview loop (Vercel, Netlify, Cloudflare Pages, Render Static Sites, GitHub Pages). A developer pushes a branch, the platform builds the static site and deploys it to a preview URL (https://pr-123.example.com). The preview URL is shareable, the preview URL is on a real domain, the preview URL is on real CDN. The pattern is the right answer for a code review, a stakeholder demo, a client approval, a QA cycle, and the pattern is the lever that turns “I can show this on my machine” into “the customer can see this on a real URL.”

The qrcode / screen-share / mobile-test loop. A developer generates a QR code for the preview URL (npx qrcode https://pr-123.example.com), opens the QR code on a phone, and tests the page on a real device. The pattern is the right answer for a developer who wants to test on a real phone without the friction of typing the URL, and the pattern is the lever that turns “I tested on my desktop” into “I tested on a real phone.”

The three ways are the floor. There is also the “Loom / screen recording” loop (the developer records a video walkthrough and shares it with the team), the “Sip / Vapi / BrowserStack” loop (the developer tests on real devices in the cloud), and the “ngrok + custom domain” loop (the developer exposes a local server under a real domain for a more polished preview). The three are the ones a developer should know first, and the three are the ones a developer can rely on.

How this fits the rest of the stack

A static website rarely lives in isolation. The static website is usually part of a stack (an API, a database, a worker, a form) that runs on a platform. The platform that handles the static website should make the rest of the stack feel like part of the same conversation.

The services layer is the part of the platform that runs the long-lived API the static website calls. The database layer is the part that holds the data the API reads and writes. The static layer is the part that hosts the static website the user sees first. The environment variables are the part that holds the secrets the build step needs at build time.

A static website on a platform where the service, the database, the storage, and the secrets are all in the same place is a static website the team is going to be able to operate. A static website on a platform where each piece is in a different console is a static website the team is going to spend the first hour just opening the right tab.

For a team that wants to see the full cost of the project before it commits, the RunxBuild hosting calculator shows the line items together. The API, the database, the storage, the static site, the worker, the bandwidth — each one is a separate number, and the team’s mental model for the platform is the sum of those numbers.

FAQ

How do I view a static website locally?

The two main ways are: open the HTML file directly in the browser (file:///path/to/index.html), or run a local server (python3 -m http.server 8000 for any directory, npx serve for a Node.js project, hugo server for a Hugo project). The local server is the right answer for a multi-page site, a site that uses fetch, or a site that uses a service worker. The file path is the right answer for a single-page site or a small change.

What is a static site generator?

A static site generator (SSG) is a tool that compiles source files (Markdown, MDX, Vue, Svelte, JSX, TSX) into static HTML, CSS, and JavaScript at build time. The popular SSGs are Astro (the right answer for most projects in 2026), Hugo (the right answer for large content sites), Eleventy (the right answer for simple static sites), Next.js (the right answer for static + dynamic hybrids), and Gatsby (the right answer for content-heavy sites with a GraphQL data layer).

Can I view a static site on my phone?

Yes. The two main ways are: open the local server on the same Wi-Fi network as the phone (http://192.168.1.100:8000, where 192.168.1.100 is the IP of the developer’s computer), or use a tunnel (ngrok http 8000https://abc123.ngrok.io), or deploy to a preview environment (Vercel, Netlify, Cloudflare Pages, Render Static Sites). The tunnel is the right answer for a quick test, the deploy preview is the right answer for a polished demo.

Why is my local server not working?

The most common causes are: the server is running on a different port than the one the developer is trying to open, the server is not running at all, the firewall is blocking the port, the node_modules is not installed, the node_modules is from a different Node version, the framework’s dev server crashed, or the browser is showing a cached version. The fix is to check the server’s output, the fix is to restart the server, and the fix is the lever that turns “the connection is refused” into “the connection is working.”

Do I need a build step for a static site?

It depends. A single HTML file does not need a build step — the developer writes the HTML, the developer serves the HTML, the developer is done. A multi-page site, a site with a CSS preprocessor, a site with a JavaScript bundler, or a site that uses Markdown for content does need a build step. The build step is the right answer for any site with more than 5 pages, and the build step is the lever that turns “I am writing raw HTML” into “I am using a framework.”

Can I use a CMS with a static site?

Yes. A static site that uses a headless CMS (Sanity, Contentful, Strapi, Decap, Tina, Storyblok, Builder.io) gets the content from the CMS at build time, the build step generates the HTML, and the static site is served from a CDN. The pattern is the right answer for a project where the content is owned by a non-developer, and the pattern is the lever that turns “the developer owns the content” into “the content team owns the content.”

How do I share a preview with a client?

The two main ways are: deploy to a preview environment (Vercel, Netlify, Cloudflare Pages, Render Static Sites), which gives the developer a real URL like https://pr-123.example.com that the developer can share with the client; or use a tunnel (ngrok http 8000https://abc123.ngrok.io), which gives the developer a public URL for the local server. The deploy preview is the right answer for a polished demo, the tunnel is the right answer for a quick test.