npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details


  • User packages



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.


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




Customizable form component




Form Component

Live Demo

A simple React form component to gather data from users.

The component takes a fields prop and generates a form interface. When the user clicks the "Submit" button, the onSubmit callback is called with a responses object.

Server requests are not within the scope of this componenet; you can do whatever you like with the data that is returned.

There are a few extra features like validation for required fields, all detailed below.

The component comes with a set of default styles, which you can override if you like.

If you have a feature request, please open an issue and we'll get to it asap.


yarn add @exodus/react-form

You will also need some CSS to make it look right. This should be included alongside the component. An example theme can be found in styles.scss

Input Types

The live demo demonstrates all supported input types:

  • short_text: Simple text input.
  • free_text: Paragraph input.
  • boolean: Displays two radio inputs with "Yes" & "No" labels.
  • dropdown: Provide an array of choices, see below example for details. An option with the text Select an option… will be added automatically as the first option. If the field is required, something other than this option must be selected.
  • multiple_choice: If single_answer is set to true, it displays the options as radio inputs, if not then as checkboxes.
  • date: Returns a date in the format yyyy-mm-dd
  • numeric: Accepts float as well as int values.
  • file: Provide an array of accepted file types when using this type.

Example Usage

The following is a complete example which includes:

  • A sample array passed as the fields prop, showing what data to include for each input type. For example, the file type expects a supported_file_types array.

  • Usage of the loading prop: The "Submit" button is deactivated when set to true. (optional)

  • Usage of showWarning and warningText props: Shows warning text beneath the "Submit" button. (optional)

  • Beneath the code example, there is a sample responses object. This is what you can expect the onSubmit callback to receive.

import React from 'react'
import ReactForm from '@exodus/react-form'
import '~/styles/react-form.scss'

// Example fields data
const fields = [
    body: 'Full name', // Question text
    type: 'short_text',
    required: true,
    id: '22e55a',
    body: 'Email',
    type: 'short_text',
    required: true,
    validate: 'email',
    id: '22e55e',
    body: 'Summary',
    type: 'free_text',
    required: false,
    id: '22e55f',
    id: '22e55g',
    type: 'file',
    body: 'Resume',
    supported_file_types: ['pdf', 'doc', 'docx', 'jpg', 'png'],
    required: false,
    body: 'Test Yes/No',
    type: 'boolean',
    required: true,
    id: '22e560',
    body: 'Test Dropdown',
    type: 'dropdown',
    required: true,
    choices: [
      { body: 'choice 1', id: '107f36' },
      { body: 'choice2', id: '107f37' },
    id: '22e561',
    body: 'Test Multiple choice',
    type: 'multiple_choice',
    required: true,
    choices: [
      { body: 'choice 1', id: '107f38' },
      { body: 'choice 2', id: '107f39' },
    id: '22e562',
    body: 'Test Multiple choice (single answer)',
    type: 'multiple_choice',
    required: true,
    single_answer: true,
    choices: [
      { body: 'choice 1', id: '107f3a' },
      { body: 'choice 2', id: '107f3b' },
    id: '22e563',
    body: 'Test Date',
    type: 'date',
    required: true,
    id: '22e564',
    body: 'Test Number',
    type: 'numeric',
    required: true,
    id: '22e565',

class MyPage extends React.PureComponent {
  constructor(props) {

    this.state = {
      requestPending: false,
      showWarning: false,
      warningText: '',

    this.onSubmit = this.onSubmit.bind(this)

  onSubmit(responses) {
    // do whatever, such as parse the response and send to a server

      requestPending: true,

    fetch(`/submit-form`, {
      method: 'post',
      body: JSON.stringify({ shortcode: '1234', data: responses }),
      .then((response) => {
        if (response.status === 200) {
          window.location.href = '/job-application-submitted'
      .catch((err) => {

          requestPending: false,
          showWarning: true,
          warningText: 'There was an error and the application was not submitted!',

  render() {
    const { requestPending, showWarning, warningText } = this.state
    return (

export default MyPage

Example responses object passed to the onSubmit callback as its first parameter. The object keys are the ids that were passed to the component with the fields prop. For required fields, isSet will always be true.

  // short_text
  '22e55a': { value: 'Barty Crouch', isValid: true, isSet: true },

  // short_text (validate: 'email')
  '22e55e': { value: '[email protected]', isValid: true, isSet: true },

  // free_text (required: false)
  '22e55f': { value: '', isValid: true, isSet: false },

  // file
  '22e55g': { value: { name: 'file.txt', data: 'alskdfj;ak' }, isValid: true, isSet: true },

  // boolean
  '22e560': { value: false, isValid: true, isSet: true },

  // dropdown
  '22e561': { value: ['107f36'], isValid: true, isSet: true },

  // multiple_choice
  '22e562': { value: ['107f38', '107f39'], isValid: true, isSet: true },

  // multiple_choice (single_answer: true)
  '22e563': { value: ['107f3a'], isValid: true, isSet: true },

  // date
  '22e564': { value: '2020-01-24', isValid: true, isSet: true },

  // numeric
  '22e565': { value: 123, isValid: true, isSet: true },