npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mmth/routes

v0.3.0

Published

A dynamic routing engine for React Router.

Downloads

38

Readme

Routes

A dynamic routing engine for React Router.

Huge thanks to both the Remix team and the Tanstack team for inspiration on this method and pioneering the file and virtual based routing that makes this workflow possible.

Philosophy: Flat files, nested folders, and virtual routes are all the same. You reach for whichever one fits the current complexity or team, and going from one to the other is just a rename and a move. No structure is imposed; the point is to support quick prototyping evolving into a big codebase.

Usage

Integration is simple:

// app/routes.ts
import { routes } from "@mmth/routes";

export default routes();

That's it! File based routes work immediately. Want to customise?

// With manual routes and options specified.
import type { RouteConfig } from "@react-router/dev/routes";
import { routes, route, index } from "@mmth/routes";

export default routes(
	[index("home.tsx"), route("/special", "./special-handler.tsx")],
	{
		directory: "",
		extensions: ["tsx", "ts", "mdx", "md"],
	},
) satisfies RouteConfig;

Naming conventions

Everything is a segment. Flat files and folders resolve to the same internal primitive, and all segments resolve to a RouteConfig-compatible node.

| Source | Result | | ---------------------- | -------------------------------------------------------------------------------------------------------------- | | index.tsx | Index route for the parent path | | page.tsx | /page | | page.about.tsx | /page/about — dots are segment separators | | folder/page.tsx | /folder/page — folders and dots are interchangeable | | $param.tsx | Dynamic param → :param | | $.tsx | Catch-all / splat → * | | (name).tsx | Optional segment → name? | | ($param).tsx | Optional param → :param? | | [sitemap.xml].tsx | Brackets escape special characters → literal sitemap.xml | | layout.tsx | Wraps every route in its folder (no path segment) | | (group)/ | Pathless grouping folder; no URL contribution | | _name.tsx | Named layout module (wraps nothing by itself) | | _name.page.tsx | page wrapped by the _name layout | | _name/ | All routes inside wrapped by the sibling _name.tsx layout | | name_.tsx | Appending an underscore to a route excludes it from the enclosing layout.tsx and from a parent route module" | | (group)_/ | Same as above but with a folder, excluding the folder and it's children | | routes.ts (in a dir) | Sub-routes file: the folder owns itself (see below) |

Dots in filenames are segment separators. So api.auth.$.tsx, api/auth.$.tsx, and api/auth/$.tsx all produce the same route. These are interchangable and all valid.

Inspired by this excelent talk by Brooks, a routes.ts in a subdirectory causes that directory to be do it's own route declaration. Sometimes you want certain routes to have their own conventions. The default export gets automatically merged into the top level, and so the subfolder can use pure manual routes, a different routing engine, or whatever they prefer.

Example of natural evolution

# Start simple
routes/
	api.auth.$.tsx

# Grow naturally as needed
routes/
	api/
		auth.$.tsx
		assets/
			index.tsx
			upload.tsx

Layouts

Two distinct layout mechanisms are available. As with everything these are interchangable and can coexist in the same directory.

  1. layout.tsx - wrapping layout**

Placing a layout.tsx inside any folder (or (group) folder) wraps every route in that folder. You might recognise this as Next.js' layout convention.

  1. _named - named layout A named layout only wraps routes that share it's name. This is how flat routes in RR/Remix have worked traditionally. _dashboard.tsx wraps all sibling routes prefixed with _dashboard:
routes/
	_dashboard.tsx
	_dashboard.home.tsx
	_dashboard.projects.tsx
	about.tsx

And organised in folders:

routes/
	_dashboard.tsx
	_dashboard/
		home.tsx
		settings.tsx
	about.tsx

And as always, you can combine all methods no problem:

routes/
	_dashboard.tsx
	_dashboard/
		home.tsx
		settings.tsx
	_blog.tsx
	_blog.blog/
		index.tsx
		$postId.tsx
	(marketing)/
		layout.tsx

Configuration

The routes helper function does all the heavy lifting for you, and while it comes with sensible defaults you can configure them all.

Here are the options with their default values:

export default routes([], {
	directory: "routes", // the directory the scanner looks for routes in.
	extensions: ["tsx", "ts", "jsx", "js"], // the file extensions of routes
	prefix: "", // the prefix to wrap all scanned routes in, mainly useful for subfolders.
	scan: true // whether to use file based routes at all
});

Setting scan: false in a sub-routes file turns off file-based routing for that folder and everything below it. The parent still excludes the folder, and any manual routes declared there are still merged in.

Other helper functions

The package comes with it's own index, route, prefix, and layout helper functions. They serve the exact same purpose as React Router's, the only difference is that they're by default relative to the folder set in the routes() options.