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

gemkit

v0.2.4

Published

create-gemkit-app util library.

Readme

Gemkit

Description

Gemkit is a JavaScript framework/library for building user interfaces fast. It uses functional components with ESM imports and exports, making the code readable and modern.

Getting started

To create a new Gemkit app, you can use the CLI command:

npx create-gemkit-app@latest your-project

That will generate a starter folder structure with Vite, Tailwind and TypeScript already integrated. After running the following commands:

cd your-project
npm install

your folder strucure should look like this:

your-project/
    node_modules/
    package-lock.json
    package.json
    src/
        components/
            Counter.ts
        pages/
            Home.ts
            NotFound.ts
        App.ts
        main.ts
        index.css
    index.html
    tsconfig.json
    vite.config.js

Development

Being able to view the webpage as you're building it is very important. That's why Gemkit uses Vite Dev Server for fast hot reloads and quick build time.

For development, run the dev command:

npm run dev

which will launch the Vite Dev Server on port 5173 (if available). You can also use:

npm run preview

to see a preview of your web app from the dist/ folder.

Building for production

If your app is ready for production, you can use the build command:

npm run build

This will create a dist/ folder, where you can find all your bundled HTML/CSS/JS code, that you can deploy on any webhosting service (ex. Vercel, Github Pages, Netlify).

Key concepts

⚡ Functional components

Gemkit uses functional components to build UI's like a house - brick by brick.

import { H1 } from 'gemkit/elements';

export default function HomePage() {

    return H1({
        children: ['This is my awesome app!']
    });
}

🛠️ Custom components

You can also create custom components and pass props to them. You might want to consider using the global state manager to avoid prop drilling.

import Foo from './components/Foo.ts';

export default function App() {

    return Foo({
        bar: 'baz'
    });
}

Then the Foo component:

import { H1 } from 'gemkit/elements';

export default function Foo({ bar }) {

    return H1({
        children: [bar]
    });
}

Gemkit also provides you with some built in components:

import { List } from 'gemkit/elements';

export default function App() {

    const todos = ['Go shopping', 'Do the dishes', 'Walk the dog'];

    return List({
        list: todos,
        fn: (todo, i) => Span({
            children: [todo],
            onClick: () => console.log(`${i}th todo`)
        })
    });
}

🔀 Hash router

Gemkit uses a hash routing system to allow routing on single page applications.

Use it as a return statement in your root (App) component. The Router takes 3 arguments:

  1. Static routes
  2. Dynamic routes
  3. Fallback (not found)
import { HashRouter } from 'gemkit/router';
import Home from './pages/Home.ts';
import About from './pages/About.ts';
import User from './pages/User.ts';
import NotFound from './pages/NotFound.ts';

export default function App() {

    return HashRouter(
        {
            '/': Home,
            '/about': About
        },
        {
            '/user': User
        },
        NotFound
    );
}

With dynamic routes, the router will automaticly pass in the :id as an argument.

For example, https://www.yoursite.com/user/abcfoobarbazxyz:

import { fetchUser } from '../lib/fetch.ts';
import { withState } from 'gemkit/hooks';
import { H1 } from 'gemkit/elements';

export default function User(uid) {

    const [user, setUser] = withState<Record<string, any>>('user', null);

    withEffect(() => {
        fetchUser(uid).then(setUser);
    }, [uid]);

    return H1({
        children: user ? [`Welcome, ${user}!`] : ['Loading...']
    });
}

🪝 Hooks

withState

All state is stored in a global object. You can have state in your application by using the withState hook, which takes 2 arguments:

  1. Key (unique identifier)
  2. Initial value

withState is also a generic function, which means you can type your return values for better type safety.

The hook then returns an array with 2 values, where the 1st one is the value and the 2nd is the setter function. Use the setter function to update the value, never modify the value on it's own.

You can access a global state by using the same key (to avoid prop drilling), or create a new one by using a unique key.

Keep in mind, that updating the state rerenders the whole application, not just the component. All code inside a component (like the console.log below) will execute on every rerender.

import { withState } from 'gemkit/hooks';
import { Button } from 'gemkit/elements';

export default function Counter() {

    const [count, setCount] = withState<number>('count', 0);

    console.log(`Count: ${count}`);

    return Button({
        children: ['Increment'],
        onClick: () => setCount(count + 1)
    });
}

withEffect

Effects are callback functions that get re-executed, when a certain state changes. You can achive this by using the withEffect hook, which takes 2 arguments:

  1. Callback (the function you want to re-execute)
  2. Dependency array

The callback is of type () => void. The dependency array should contain all your dependent variables (states). When any one of these variables change, the callback gets re-executed. You can put whatever you want to execute on every re-render outside the hook.

withRef

The withRef hook is used to get a reference. In the example below, we're using it to get the uncontrolled input's value. withRef returns an object:

{ current: null }

which you can modify with the ref.current property.

import { withState, withRef } from 'gemkit/hooks';
import { Div, H3, Input, Button } from 'gemkit/elements';

export default function InputPage() {

    const [text, setText] = withState<string>('');
    const inputRef = withRef<HTMLInputElement>();

    return Div({
        children: [
            H3({ children: [text] }),
            Input({
                ref: inputRef,
                type: 'text',
                placeholder: 'Enter some text...'
            }),
            Button({
                children: ['Display input'],
                onClick: () => setText(inputRef.current?.value ?? '')
            })
        ]
    })
}

Tips

Use ternary operators

For conditional rendering, you can just simply use ternaries, like in this example:

import { H1 } from 'gemkit/elements';
import { isLoading, content } from './lib/util.ts';

export default function App() {

    return H1({
        children: isLoading ? ['Loading...'] : [content]
    });
}

📞 Contact

Have any questions or suggestions? 📧 Email: [email protected] 📬 Or open an issue right here on GitHub.