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

@meta-ultra/app-router

v1.1.1

Published

Next.js App Router like routing convention for pure client rendering React application

Downloads

26

Readme

Welcome to @meta-ultra/app-router

With @meta-ultra/app-router, it's not only possible to structure the application routing in a meaningful, intuitive and highly-maintainable way as the Next.js App Router does, but also allow for improving user experience effortlessly.

If you're familiar to Next.js 13 or later version, or you've got stuck in providing great UX with react-router, @meta-ultra/app-router will be the place where you're looking forward.

Give a ⭐️ if this project helped you!

🌟 Features

  • Follows the file conventions and functionalities of Next.js App Router, such as page, layout, template,loading, not-found, error and route.
  • Supports receiving params and searchParams as props in page, layout.
  • Supports dynamic routes including catch-all segments and optional catch-all segments.(by setting route id)
  • Supports default root layout.
  • Supports advanced feature - parallel routes with its own template, loading, not-found and error.
  • Supports advanced feature - fully-functional intercepting routes start with (.), (..), (..)(..) and (...).
  • Besides the context free notFound as Next.js does, an extra notFound with context created by useNotFound() is available.
  • It's capable of setting the document.title, and other metadata like description, keywords and author through metadata or generateMetadata APIs as Next.js does on Server Components.(At this moment, only basic fields are supported)
  • Supports route handlers including dynamic route feature.
  • Based on React Router v6, it's free to structure the application routing and project file-system hierarchy, although it's recommended to abide by the rules of Next.js App Router.
  • @meta-ultra/app-router is written with type safety in mind through TypeScript.

🏠 Installation

Install @meta-ultra/app-router with your favorite package manager:

  • pnpm: pnpm add @meta-ultra/app-router@latest
  • yarn: yarn add @meta-ultra/app-router@latest
  • npm: npm install -S @meta-ultra/app-router@latest

✨ Usage

Let's demonstrate how to use @meta-ultra/app-router with Next.js App Router conventions.

  • Project structure:

    /src
     - router.tsx
     - /app
      - global-error.tsx
      - layout.tsx
      - loading.tsx
      - not-found.tsx
      - page.tsx
      - (system)
        - layout.tsx
        - /users
          - page.tsx
        - /posts
          - error.tsx
          - loading.tsx
          - page.tsx
  • Defines routes object with RouteSegmentElement:

    // ./src/router.tsx
    import { lazy } from "react";
    import { createHashRouter } from "react-router-dom";
    import {
      RootLayoutRouteElement,
      RootErrorElement,
      LayoutRouteElement,
      PageRouteElement,
    } from "@meta-ultra/app-router";
    import RootLoading from "./app/loading"
    import RootNotFound from "./app/not-found"
    import GLobalError from "./app/global-error"
    import PostsLoading from "./app/(system)/posts/loading"
    import PostsError from "./app/(system)/posts/error"
    
    const router = createHashRouter([
      {
        path: "/",
        element: (
          <RootLayoutRouteElement 
            loading={RootLoading} 
            error={GlobalError}
            notFound={RootNotFound}
          >
            {lazy(() => import("./app/layout"))}
          </RootLayoutRouteElement>
        ),
        errorElement: (<RootErrorElement notFound={RootNotFound} />)
        children: [
          {
            index: true,
            element: (<PageRouteElement>{lazy(() => import("./app/page"))}</PageRouteElement>),
          },
          {
            element: (
              <LayoutRouteElement>
                {lazy(() => import("./app/(system)/layout"))}
              </LayoutRouteElement>
            ),
            children: [
              {
                path: "users",
                element: (<PageRouteElement>{lazy(() => import("./app/(system)/users/page"))}</PageRouteElement>),
              },
              {
                path: "posts"
                element: (<PageRouteElement loading={PostsLoading} error={PostsError}>{lazy(() => import("./app/(system)/posts/page"))}</PageRouteElement>),
              }
            ]
          }
        ]
      }
    ])
    export default router
  • Defines the root layout, loading, not-found and global-error.

    // ./src/app/layout.tsx
    import { type FC } from "react"
    import { type GenerateMetadata, useNotFound } from "@meta-ultra/app-router"
    import { useLocation } from "react-router-dom"
    
    // Naming starts with `use` to pass the validations of using React hooks.
    export const generateMetadata = async function useGenerateMetadata(
      { params, searchParams }, 
      parentMetadataPromise
    ) {
      const location = useLocation()
      const notFound = useNotFound()
    
      // Notice that, the second parameter is an instance of Promise here, 
      // rather not Metadata plain old object in Next.js.
      const parentMetadata = await parentMetadataPromise
    
      // It's able to do some authorization works, except changing application metadata.
      const perms = await getPermissions()
      const perm = permissions.find(perm => perm.pathname === location.pathname)
      if (!perm) {
        return notFound()
      }
    
      return {
        title: perm.title,
        description: perm.description,
        keys: parentMetadata.keys,
      }
    } 
    
    const RootLayout: FC<PropsWithChildren> = ({children}) => {
      return children
    }
    export default RootLayout
    
    // ./src/loading.tsx
    export default function RootLoading() {
      return <div>Loading...</div>
    }
    
    // ./src/not-found.tsx
    import { type FC } from "react"
    import { type ErrorResponse } from "@meta-ultra/app-router"
    import { useNavigate } from "react-router-dom"
    
    const RootNotFound: FC<{error: ErrorResponse}> = ({error}) => {
      const navigate = useNavigate()
      const onReset = () => {
        /**
         * NOTE:
         * Due to the client router has been broken when not found occurs,
         * change the client router to home page first, and then refresh browser 
         * to reinitialize the whole client router from scratch.
         */ 
        navigate("/")
        location.reload()
      }
    
      return (
        <main>
          <p>{error.message}</p>
          <button onClick={onReset}>Go to home page!</button>
        </main>
      )
    }
    export default RootNotFound
    
    // ./src/global-error.tsx
    import { type FC } from "react"
    import { type ErrorProps } from "@meta-ultra/app-router"
    
    const GlobalError: FC<ErrorProps> = ({error, reset}) => {
      return (
        <main>
          <p>{error.message}</p>
          <button onClick={() => reset()}>Click to reset!</button>
        </main>
      )
    }
    export default GlobalError

👶 Author

Hey, friends. I'm John Huang, a full stack developer majorly code with React, Next.js, GraphQL, TailwindCSS, Taro and SpringBoot. Feel free to contact with me 😃

🤝 Contributing

Contributions, issues and feature requests are welcome! Feel free to check issues page.