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

@pickjunk/min

v1.11.2

Published

A Tiny React Framework

Readme

min

NPM Version Build Status NPM Downloads

A Tiny React Framework. Routing, SSR, out-of-box webpack & babel configuration, make it work, but not annoying.

Quick Start

A mobile example with zarm:

$ npm init @pickjunk/min create mobile min-mobile

An admin example with antd:

$ npm init @pickjunk/min create mobile min-admin

Routes.js

Actually, the type definition of MIN's route is:

type Route {
  path?: string;
  name?: string;
  component: string;
  children?: Route[];
}

It's simple. With some features you might care about:

  • path will be concated to a complete route path without any implicit '/'. In other words, any '/' should not be ignored. You should write it explicitly in any time.

  • name is a useful but not required property. In other react routers, it's usually considered as a feature called Named Route for convenient imperative navigation. More detail is below in Imperative Router.

  • component path is always relative to routes.js.

  • Depth-First is the router matching strategy.

  • Default Route (the route without path) supported. If the matching complete in the parent of the default route, router will try to search the children of the parent. Once it check a route without path property, the default route is found.

Dynamic Routes

There are two ways to define a dynamic route:

  • Short Variable. It looks like :var. Convient, but not flexible. Noticed that it must end with a '/' or be the end of the route path. Otherwise, unexpected behavior will occur.

  • Regexp Variable. The format is (var:regexp). It's powerful but escaping consideration may be annoying. In fact, :var is equal to (var:[^/]+).

Router Arguments

Once you define a dynamic route, it's easy to get its arguments by useRouter hook.

import React from 'react';
import { useRouter } from '@pickjunk/min';

export default function World() {
  const {
    args: { who },
  } = useRouter();

  return <span>{who}</span>;
}

Imperative Router

push, replace, forward, back, go, link, six useful methods exported for imperative router actions.

import { router } from 'react';

// navigate with history.push
router.push('name or path', { args: 'args for named routes' });

// navigate with history.replace
router.replace('name or path', { args: 'args for named routes' });

// equal to history.forward
router.forward();

// equal to history.back
router.back();

// equal to history.go
router.go(2);

// generate url to render, very useful for named routes
router.link('name or path', { args: 'args for named routes' });

Production

It's really simple to release a MIN app.

// package.json
// Add build & start script.
"scripts": {
  "build": "min build -p",
  "start": "min start"
},
$ npm run build
$ npm start

That's all. A production server with SSR enabled is ready on port 8000.

Because of the demand of SSR and dynamic routes, the production server is necessary. In other words, MIN app can not be released as pure static resources without a matching server. It might be heavier than many traditional frontend frameworks. But I think it worth.

Additionally, you might need to optimize the bundles in production. Here is the analyze command:

// package.json
// Add analyze script.
"scripts": {
  "analyze": "min analyze"
},
$ npm run analyze

Command Line Tools

More defails for min can be found in min's help manual.

// package.json
// Add min script.
"scripts": {
  "help": "min"
},
$ npm run min

cli

Reversed Log

You might have noticed that when you run min dev, a logger is enabled by default:

cli

It's MIN's "Reversed Logger" developed based on a thought that simple browser logger (console.log) is not good enough in a web app, especially in production. In most of cases, we need to collect browser logs and report them to the server finally persisting the logs.

By default, MIN enable the reversed logger and provide the basically navigating log.

To use reversed logger in your code:

import { log } from '@pickjunk/min';

log.trace('Object or String');
log.debug('Object or String');
log.info('Object or String');
log.warn('Object or String');
log.error('Object or String');
log.fatal('Object or String');

To config your logger:

// create min.config.js in your project root directory

let config = {
  log: {
    // Optional. Null by default, means console. String, means file path.
    file: process.env.NODE_ENV === 'production' ? 'log/app.log' : null,
    // Optional. '/__log__' by default.
    endpoint: '/__log__',
  },

  // disable the logger
  log: false,
};

module.exports = config;

Advanced (Under the Hood)

The content from here on is not necessary to read. But if you are curious about how MIN works under the hood, topics below may be a good guide for you.

Code Splitting

MIN provide a simple and intuitive approach to concrete code splitting. How it works can be explained in one sentence - the component property of every Route node in routes.js will be transformed to a require.ensure loading function.

Yes, transform, a special webpack loader do this work to routes.js. No need to concern about code splitting too much because this behavior is good enough in most cases.

The only thing you should notice is always naming routes.js as 'routes.js'. Because the test rule of the webpack loader depend on it.

Better Router

There are two problems with that many other react router does not do well:

  • Unintuitive reconciliation for dynamic route components. The most popular case is defining a route like { path: '/post/:id', component: './Post' }. In many other react router, the Post Component will not be unmounted and then mounted in a navigation from /post/1 to post/2. It's really annoying because most people will write the data loading logic in component mounted hook and do not expect a extra prop listener for refreshing data. React Hooks (useEffect) may help you out nowadays. But honestly, it's still not intuitive.

  • Race condition in async route loading. Once you trigger two different navigations almost in the same time, this problem can't be ignored. If the former one completed after the latter one, "rollback" shows.

To be a better router, MIN has fixed these two problems:

  • All Route Components take their path as their key prop. For example above, <Post key={'/post/1'} /> and <Post key={'/post/2'} /> will be rendered. Tell react to reconcile intuitively.

  • Add switchMap operation to routing pipeline. Thanks to rxjs, race condition is fixed so elegantly. If you isn't familar with rxjs, a document and a marble diagram may help you.

SSR (Server Side Render)

Complicated topic. It contains many details about engineering but not theory.

Without doubt, MIN use ReactDOMServer.renderToString and ReactDOM.hydrate. But when facing to the problem about how to make a pretty app entry, which only export necessary interfaces but not export any annoying tech details about SSR, work become hard. You can't see ReactDOMServer.renderToString and ReactDOM.hydrate but a simple app() entry nowadays. MIN take all its effort to make it be.

The main code about SSR is in bin/ssr.js, src/index.ts and src/Router.ts. In development environment, situations in bin/dev.js is more complicated. Thanks to the serverSideRender option of webpack-dev-middleware, work should have been harder without it.

Besides, like other SSR supported frameworks, MIN also support the initialProps feature:

import React from 'react';
import { initialProps } from '@pickjunk/min/Router';

export default initialProps(function({ path, args, name }) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve({ path, args, name });
    }, 2000);
  });
})(function One({ path, args, name }) {
  return (
    <>
      <h4>This is one</h4>
      <h4>
        path:{path} args:{JSON.stringify(args)} name:{name}
      </h4>
    </>
  );
});

initialProps not only run in browser but also in SSR server. Make sure the code in it is isomorphic. Fetching data in initialProps and return a Promise result, a "pure" SSR app is born.

initialProps is a powerful feature. But it require isomorphic code which usually makes things complicated. "Pure" SSR app is not necessary in most cases. So it's not recommanded.