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 🙏

© 2024 – Pkg Stats / Ryan Hefner

qwik-router

v1.3.1

Published

A SPA-like router for Qwik.

Downloads

62

Readme

Table of Contents

Why?

Many Qwik native solutions, like routing, are very attached to Qwik City, which is great for most use cases. But sometimes, more than a file-based routing is needed.

Possible use cases are:

  • Micro-frontends with Qwik, where you need to load different apps on the same page
  • When you need a component-based routing with customized rules.
  • When you need multiple routers on the same page.

Thinking of that, I created this router to be used with Qwik based on similar projects like vue-router and react-router.

This follows Qwik principles and is targeted for flexibility. If you don't have unique routing needs, you should use Qwik City.

This component has been used in production in a few projects but is still in beta.

I'll add more documentation and examples in the next few days.

See a Working Example

git clone https://github.com/dannyfranca/qwik-router.git
pnpm i
pnpm nx run qwik-router:serve

Installing in your Project

pnpm i qwik-router

or

yarn add qwik-router

or

npm i qwik-router

Initiating the router

To initiate the router, import the initRouter and execute in the Root component giving a string URL as input.

This will create a Qwik context that will be changed every time the app navigates using the navigateTo, the Link component or the window object popstate event.

// root.tsx
import { component$ } from "@builder.io/qwik";
import { initRouter } from "qwik-router";

export default component$((props: { url: string }) => {
  initRouter(props.url);

  return <Child />;
});

Reacting to Route Changes

The route state is a reactive Qwik store with the interface RouteState shared with a Qwik context.

Import the useRoute and/or useParams and set up a lifecycle task.

⚠️ Warning:

The useParams hooks only works under a Router component.

import { useVisibleTask$, useTask$, component$ } from "@builder.io/qwik";
import { isServer } from "@builder.io/qwik/build";
import { useRoute, useParams } from "qwik-router";

export default component$(() => {
  const routeState = useRoute();
  const paramsState = useParams(); // only works under a Router component

  useVisibleTask$(
    ({ track }) => {
      track(() => routeState.pathname);
      track(() => routeState.search);
      // react to pathname and search changes only when component becomes visible on the browser.
    },
    {
      strategy: "intersection-observer",
    }
  );

  useTask$(({ track }) => {
    track(() => paramsState);
    // react to any changes on the path parameters
  });

  useTask$(({ track }) => {
    track(() => paramsState.id);
    // react to any changes on the :id path parameters
  });

  useTask$(({ track }) => {
    track(() => routeState);
    if (isServer) {
      // execute some code before components render on the server
    } else {
      // react to any changes on the browser
    }
  });

  return <Child />;
});

The initRouter also returns a router reactive state, just like useRouter.

Notice the route state is based on the native URL API. It has full interface compatibility with the native URL, but it is not an instance of URL.

// root.tsx
import { component$ } from "@builder.io/qwik";
import { initRouter } from "qwik-router";

// notice that initRouter can only be executed under an optimized component. A.K.A `component$`
export default component$((props: { url: string }) => {
  const router = initRouter(props.url);
  router.hash; // #hash
  router.pathname; // /path
  router.search; // ?query=string
  router.query; // { query: 'string' }
  router.host; // host:port
  router.origin; // http://host:port
  router.protocol; // http:
  router.port; // port
  router.href; // http://host:port/path?query=string#hash

  return <Child />;
});

Navigating

The useNavigate Hook

This hook creates a navigation function.

import { component$ } from '@builder.io/qwik';
import { useNavigate } from 'qwik-router';

const App = component$(() => {
  const nav = useNavigate();

  return <Child onClick$={() => {
    nav('/new-path?param=value#new-hash')
  }} />;
});

The Link Component

Use the Link component like an anchor tag. It uses the useNavigate hook under the hood to navigate.

You can add an optional activeClassName prop to config the class that will be added when the link matches the active path.

import { component$ } from '@builder.io/qwik';
import { Link } from 'qwik-router';

const NewPathNav = component$(() => {
  return <Link activeClassName="active" href="/new-path">Go to New Path</Link>;
});

Component Routing

Inspired by the API of react-router and vue-router, the component routing uses the same underlying engine to identify the router and route to a configured component.

All you have to do is create a route config file and serve the Router component with the routes properties.

It is important to attribute the Component function to the component prop, not the JSX instance.

// routes.ts
import { RouterConfig } from 'qwik-router';
import { RouteComponent1 } from './components/RouteComponent1';
import { RouteComponent2 } from './components/RouteComponent2';
import { RouteComponent3 } from './components/RouteComponent3';

const routes: RouterConfig = [
  {
    path: '/:lang?/route-1', // Optional lang parameter. Matches /route-1 and /en/route-1
    component: RouteComponent1,
  },
  {
    path: '/:lang/route-2',
    component: RouteComponent2,
  },
  {
    path: '/:lang/route-3',
    component: <RouteComponent3 />, // Wrong
  },
];

The Router component has an optional defaultComponent property to be used when no path is matched.

// App.tsx
import { component$ } from "@builder.io/qwik";
import { initRouter, Router } from 'qwik-router';
import { routes } from './routes.ts';
import { DefaultRouteComponent } from './components/DefaultRoute';

const App = component$((props: { url: string }) => {
  initRouter(props.url);

  return <Router routes={routes} defaultComponent={DefaultRouteComponent} />;
});

Matching Patterns

To check all possible advanced matching patterns, check the path-to-regexp documentation.

Multiple Routers and URL Parameters

The router supports URL parameters. You can access them in the component with the useParams hook.

The reason not to use the useRoute hook is because you can use multiple Route components, leading to different param states.

The Router initializes the param state as a Context, so it can be accessed by any component under it using the useParams hook.

// root.tsx
import { component$ } from "@builder.io/qwik";
import {
  type RouterConfig,
  initRouter,
  useParams,
  useRoute,
  Router,
} from "qwik-router";

const DynamicRoute = component$(() => {
  const route = useRoute();
  const params = useParams(); // { language: 'en', page: 'home' }
  return <div>I am DynamicRoute</div>;
});

const Route1 = component$(() => {
  const route = useRoute();
  const params = useParams(); // { lang: 'en' }
  return <div>I am Route 1</div>;
});

const Route2 = component$(() => {
  const route = useRoute();
  const params = useParams(); // { lang: 'en' }
  return <div>I am Route 2</div>;
});

const routeConf1: RouterConfig = [
  {
    path: "/:language/:page",
    // /en/home -> useParams() will return { language: 'en', page: 'home' }
    component: DynamicRoute,
  },
];

const routeConf2: RouterConfig = [
  {
    path: "/:lang/route-1",
    // /en/route-1 -> useParams() will return { lang: 'en' }
    component: Route1,
  },
  {
    path: "/:lang/route-2",
    // /en/route-2 -> useParams() will return { lang: 'en' }
    component: Route2,
  },
];

export default component$((props: { url: string }) => {
  initRouter(props.url);

  return (
    <>
      <Router routes={routeConf1} />
      <Router routes={routeConf2} />
    </>
  );
});

Roadmap

  • [x] Dynamic Route Matching
  • [x] Params in URL
  • [x] Advanced Matching Patterns
  • [x] Router Component
  • [x] Link Component
  • [x] Programmatic Navigation
  • [x] Route State Compatible with native URL
  • [ ] Navigating State
  • [ ] Properties to Routes
  • [ ] Scroll Behavior
  • [ ] Transition Animations
  • [ ] Navigation Guards
  • [ ] Route Meta Fields
  • [ ] Named Routes
  • [ ] Named Views
  • [ ] Passing Props to Routes
  • [ ] Nested Routes
  • [ ] Different History Modes
  • [ ] more...

⭐ Give us a Star ⭐

It was quite a challenge to build this project, and we are very proud of it. We hope you enjoy it too.

If this project generated value for you, please give us a star on GitHub. It helps the project be discovered by other people.

Contributing

Want to contribute? 😀

Please read and follow our Contributing Guidelines to learn what are the right steps to take before contributing your time, effort and code.

Thanks 🙏

Code Of Conduct

Be kind to each other and please read our code of conduct.

Related Links

License

MIT