StencilJS Routing: @stencil/router vs Native History API
Decide between @stencil/router and native History API routing for StencilJS, with setup steps, trade-offs, and browser verification.
06 Oct 2025, 17:25 UTC

Decision and constraints
You are building a StencilJS application and need navigation between views. Stencil compiles components to web components, so the browser's native History API or hash changes can technically move the URL without a framework. The decision is whether to adopt Stencil's official routing package, @stencil/router, or implement routing yourself with native browser APIs.
Useful takeaway: choose @stencil/router for most multi-view Stencil apps because it ties route matching to the compiler and supports server-side rendering (SSR). Use native routing only for isolated widgets, zero-dependency requirements, or when an external system already owns the URL.
Assumptions: you are working in a Stencil project, you control the build configuration, and you need client-side navigation without full page reloads.
Supported options compared
| Option | Setup complexity | SSR support | Custom code | Dependency | Best for |
|---|---|---|---|---|---|
| @stencil/router | Low | Supported | Minimal | Small package | Multi-view Stencil apps and design systems |
| Native History API routing | Medium | Limited or custom | High | None | Zero-dependency widgets or externally managed URLs |
Trade-offs
@stencil/router
The package provides declarative route definitions, automatic path matching, and the stencil-link component for navigation. It integrates with Stencil's build pipeline, so route changes can be pre-rendered or hydrated consistently. The cost is an additional dependency and a small bundle increase.
Native routing
Native routing means listening to popstate or hashchange events, manually toggling component visibility, and keeping the URL in sync with state. You keep dependencies at zero, but you write more glue code and lose built-in SSR conventions.
Implementation with @stencil/router
This example adds route matching to an existing Stencil app.
- Install the package. Run this in the project root with a user account that can write to
node_modulesandpackage.json:
npm install @stencil/router --save-dev
Risk: the command modifies package.json and installs third-party code. If your organization restricts registry access, use an internal mirror.
- Register the router in the Stencil config. Add the import to
stencil.config.tsat the project root:
import { Config } from '@stencil/core';
import { router } from '@stencil/router';
export const config: Config = {
// existing configuration
};
Missing this import can cause build errors, according to the package setup notes.
- Define routes in a component. Use
<Route>to map paths to components:
import { Component, h } from '@stencil/core';
import { Route, Router } from '@stencil/router';
@Component({ tag: 'app-shell' })
export class AppShell {
render() {
return (
<Router>
<nav>
<stencil-link href='/'>Home</stencil-link>
<stencil-link href='/about'>About</stencil-link>
</nav>
<Route path='/' component={Home} exact={true} />
<Route path='/about' component={About} />
</Router>
);
}
}
Angle brackets are escaped above for safe markup. In a real TypeScript file, write JSX tags directly.
- Build the project. Run:
npx stencil build
Expected check: the build completes without route-related errors. The compiler output should not warn about missing route definitions.
- Verify in a browser. Open the built app, click a
stencil-link, and confirm the URL changes and the matching component renders without a full page reload.
Diagnostic decision: which signal tells you routing works
- If clicking
stencil-linkupdates the address bar and swaps the view, @stencil/router is active. - If the URL stays the same or the browser reloads, check that
Routerwraps the routes and thatstencil-linkis used instead of anchor tags. - If the build fails, confirm
@stencil/routeris installed and imported instencil.config.ts.
Limitations
- @stencil/router adds bundle size, though it is typically small.
- It is specific to Stencil; you cannot drop
<Route>into a plain HTML page. - SSR behavior depends on your Stencil prerendering or server setup; the package alone does not guarantee server-rendered routes.
Rollback
Because installation and configuration changes project state, you can reverse them:
npm uninstall @stencil/router- Remove the router import and route JSX from
stencil.config.tsand components. - Replace
stencil-linkwith standard anchors if needed. - Run
npx stencil buildagain to confirm the app compiles without the package.
Practical check after rollback: the build should complete and the app should navigate using whatever native method you restored.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.