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

thenameisf

v1.0.6

Published

Javascript framework with both signals and React API that uses Web Components instead of JSX

Readme

F

F is a javascript front-end framework just like React with same hooks. But by using HTML custom elements (web components) instead of JSX it doesn't need a build step and is future proof.

There are also signals hooks that don't exist on the React world. I recommend using them but pick the style you prefer.

Motivation

I didn't want to learn new javascript front-end frameworks.

Alternatives

There are alternatives that use custom elements but code is so big! F implementation is short and easy to understand. Well, it was short until signals support was added.

Installation

npm i thenameisf

Usage

There are two ways, functional and class-based.

Let's consider this html for both examples:

<!doctype html>
<html>
  <head>
    <script defer src="my-app.js"></script>
  </head>
  <body>
    <my-app></my-app>
  <body>
</html>

Functional

import { f, useState, useEffect } from 'thenameisf'

// this automatically defines "my-app" custom element (used on above HTML)
f(function myApp ({ html }) {
  const [test, setTest] = useState(1)
  useEffect(() => {
    const i = setInterval(() => {
      setTest(t => ++t)
    }, 2000)

    return () => clearInterval(i)
  }, [])

  return html`<my-child props=${{ test }} />`
})

// this automatically defines "my-child" custom element
f(function myChild ({ html, h, props }) {
  // html, h, this.html and this.h are the same
  // there's also svg, s, this.svg and this.s
  return this.h`<div>This is a test: ${props.test}<div>`
})

Note that custom elements require atleast two words for the tag name, that is, "myApp" becomes "my-app", while "app" wouldn't work.

Class-based

import { F, useState, useCallback } from 'thenameisf'

customElements.define('my-app', class extends F {
  // you can use all custom element methods
  connectedCallback () {() {
    super.connectedCallback()
    console.log('connected')
  }

  component () {
    const [test, setTest] = useState(1)
    const onClick = useCallback(() => setTest(t => ++t), [setTest])

    return this.html`<ul onclick=${onClick}>
      ${[...Array(test).keys()].map(i => this.html({ key: i })`<li>${++i}</li>`)}
    </div>`
  }
})

Available React-Compatible Hooks

  • useCallback
  • useEffect
  • useInsertionEffect
  • useMemo
  • useRef
  • useState

Extra Features

useGlobalState for shared state

The useGlobalState hook is like useState but shares the state across all components if using same namespace.

The first component to call useGlobalState with a specific namespace sets the initial value.

// on my-component-a.js
const [test, setTest] = useGlobalState('a-namespace', 1)
setTest(20)

// on my-component-b.js
// test === 20
const [test, setTest] = useGlobalState('a-namespace', 1)

useClosestState hook instead of useContext

The React's useContext hook equivalent is the useClosestState one that is like useState but shares the state from the closest ancestor component that called useClosestState with same namespace and a initial value.

// on my-component-grand-grand-parent.js
const [test, setTest] = useClosestState('a-namespace', 5)
setTest(20)

// on my-component-grand-parent.js
const [test, setTest] = useClosestState('a-namespace', 1)

// on my-component.js
// test === 1
const [test, setTest] = useClosestState('a-namespace')

Simplified event callbacks

You can use the function from useState directly as an event callback without resorting to useCallback.

const [value, setValue] = useState('')
return html`<input value={value} oninput={setValue} />`

No need to mutate array if its length changes

When adding/removing an item from a useState/useGlobalState's array state, there is no need to recreate the array to detect a change. Great for infinite loading.

const [array, setArray] = useState([0])
const onClick = useCallback(() => setArray(a => {
  // instead of return [...a, a.length]
  a.push(a.length); return a
}), [setArray])
return html`<div onclick=${onClick}>Array length: ${array.length}</div>`

Updating an existing array item would need a new array, though. Unless you add the eqKey symbol as key. F will check if its value has changed to determine if the array has changed (it can also be used for other object types).

// or import { eqKey } from 'thenameisf'
const onClick = useCallback(e => {
  setArray((a, eqKey) => {
    a[e.target.dataset.index] = 'new value'
    a[eqKey] = Math.random()
    return a // instead of returning [...a]
  })
}, [setArray])

Signals

Signals are an alternative reactive programming paradigm to React's useState.

It is more performant because when a signal value changes, just components that use the value will re-render, not the component that instantiated the signal. With useState, all the tree of components starting from the useState caller would re-render.

useSignal

useSignal returns an object with .get() and .set() methods similar to useState returned array's items. It also replaces useRef. If you call .get(false), it won't cause re-renders.

We adopted a $ suffix as a convention for signal variable names so that you know that the variable has a getter and a setter.

In the example below, clicks on the parent component's button won't re-render the parent, just the child.

f(function componentA ({ html }) {
  const count$ = useSignal(0) // or useSignal(() => 0) for lazy initial value
  return html`<div>
    <button onclick=${() => count$.set(v => ++v)}
    <component-b props=${{ count$ }} />
  </div>`
})

f(function componentB ({ html, props }) {
  return html`<span>${props.count$.get()}</span>`
})

[!TIP] You can use example$() and example$(newValue) call style instead of the more verbose example$.get() and example$.set(newValue).

The eqKey may be used here too if you don't want to return a new object on signal update. For example:

// or import { eqKey } from 'thenameisf'
signal$.set((prevValue, eqKey) => {
  prevValue.newItem = 3
  prevValue[eqKey] = Math.random()
  return prevValue
  // instead of return { ...prevValue, newItem: 3 }
})

useComputed

Drop-in replacement to useMemo. Difference is it doesn't need to list dependencies. It isn't compatible with useState values (can't track their updates).

It is used like a regular signal but it doesn't have a .set() setter. Nevertheless, we adopted the same $ suffix because the difference isn't big enough to justify using another one.

When a signal (or other computation) inside it changes, it schedules a refresh of its cached computed value for when its read somewhere with .get().

// the function argument can't be async
const doubleCount$ = useComputed(() => count$.get() * 2)
console.log(doubleCount$.get())

useStore

An easy way to group the creation of signals and computations. It looks at the suffixes of the object argument keys to know what to create.

const childProps = useStore({
  // signal, because it ends with $
  count$: 0
  // computed, because it ends with $ and is initialized with a function
  doubleCount$: function () { return this.count$.get() * 2 },
  notReactive: 'Things won\'t re-render if I change',
  deep: {
    // signal
    example$: 3
  },
  aSignal$: { anotherSignal$: 'signal\'s signal' }
})
Lazy Initial Values

Pass a function to useStore to run just once any heavy computations required to initialize its values.

const childProps = useStore(() => {
  const count$ = heavyComputation() // can't return a function
  const doubleCount$ = function () { return this.count$.get() * 2 }
  const notReactive = 'Things won\'t re-render if I change'
  const deep = { example$: 3 }
  return {
    count$,
    // can't initialize a signal to a function value
    // because functions on fields with keys ending with $
    // will be used to initialize computations
    signalFunction$: undefined
    doubleCount$, // computed
    notReactive,
    deep
  }
})
// a store's signal field may be set to a function after the store initialization
useTask(() => {
  signalFunction$.set(function example () { /* ... */ })
})

Alternatively, its possible to lazy init a store's signal by passing a function with a strategy="signal" property:

const store = useStore({
  lazySignalExample$: (() => {
    // "heavyComputation" call may even return a function
    const fn = () => heavyComputation()
    fn.strategy = 'signal'
    return fn
  })()
})

useGlobalStore

Same thing as useStore but the first argument is a string that you can reference on other components. It's like useGlobalState but for stores.

// on my-component-a.js
const store = useGlobalStore('example123', { /* ... */ })

// on my-component-b.js
const sameStore = useGlobalStore('example123')

useGlobalSignal

Same thing as useGlobalStore but for storing a single signal.

const example$ = useGlobalSignal('<namespace>', <initial value>)

useGlobalComputed

Same thing as useGlobalStore but for storing a single computation.

const example$ = useGlobalComputed('<namespace>', <function>)

useClosestStore

This is closer to React's useContext behavior than useGlobalStore.

Gets the store from the closest ancestor that initialized a store with a specific namespace using useClosestStore with a second argument.

// on grandparent component
const store = useClosestStore('example123', { a$: 1 })
// on parent component
const store2 = useClosestStore('example123', { b$: 2 })

// on descendant component
const { b$ /* there's no a$ */ } = useClosestStore('example123')

useClosestSignal

Same thing as useClosestStore but for storing a single signal.

// on ancestor component
const example$ = useClosestSignal('<namespace>', <initial value>)
// on descendant component
const example2$ = useClosestSignal('<namespace>')

useClosestComputed

Same thing as useClosestStore but for storing a single computation.

// on ancestor component
const example$ = useClosestComputed('<namespace>', <function>)
// on descendant component
const example2$ = useClosestComputed('<namespace>')

useTask

Drop-in replacement to useEffect and useInsertionEffect.

Use the track function instead of the dependencies array. It can't track values from useState.

Use the cleanup function instead of returning a function.

Signature: useTask(fn, config)

Config argument (listed values are the default ones):

{
  // or 'visible' which would run just when component is visible
  when: 'init',
  // these are used for "when=visible" and work the same as the
  // options for the IntersectionObserver api
  root: null, rootMargin: '50%', threshold: 0
  // or 'rendering' which would run after the component html's first render
  // and it's good for waiting for refs set on components like <div ref=${signal$}>
  // to be available
  after: 'insertion',
  // or 'serial' which queues tasks sequentially
  execution: 'concurrent',
  // used for serial execution
  queueSize: 1
}

Example:

useTask(({ track, cleanup }) => {
  const value = track(() => aSignal$.get())

  const i = setInterval(() => {
    console.log(value)
  }, 2000)

  cleanup(() => clearInterval(i))
})

useAsyncComputed

Like useComputed, it has only a getter. The arguments and usage are identical to useTask, except that it expects you to return a value.

const computed$ = useAsyncComputed(async ({ track, cleanup }) => {
  track(() => aSignal$.get())

  const c = new AbortController()
  cleanup(() => c.abort())
  const person = await fetchPerson(c.signal)
  return person.name
})

console.log(computed$.get()) // starts undefined

You may initialize it with a value, or lazily with a function, before running the function body:

const computed$ = useAsyncComputed('Unknown Person', async () => { /* ... */ return 'Alice' })

The returned computation variable has a .promise$ signal property that is set to an object as follows:

{
  status: 'resolved' // pending / rejected
  hasValue: true, // false
  isLoading: true, // false
  error: null // thrown error
}

It may be paired with the accompanying component `:

import { f } from 'thenameisf'
import 'f/components/f-async-computed.js'

f(function myComponent ({ html }) {
  const asyncComputedExample$ = useAsyncComputed(async () => {
    const user = await fetchUser()
    return user
  })

  return html`
    <f-async-computed
      props=${{
        asyncComputed$: asyncComputedExample,
        onPending: () => this.h`Loading...`,
        onRejected: error => this.h`Error: ${error.message}`,
        onResolved: result => this.h`Success: ${result}`
      }}
    >
    <div>Can use it directly too: ${asyncComputedExample$.get() ?? 'not loaded yet'}</div>
  `
})

useUntrackedCallback

You probably won't need this because React's useCallback can already be used with signals/computeds.

const onClick = useCallback(() => signalB$.set(signalA$.get() + 1))

It works the same except that all calls to signals/computeds' .get()s are treated as if they were called as .get(false).

It could be useful if you don't control the function argument.

// same as const thisDoesntCauseRerenders = useCallback(() => console.log(signalA$.get(false)))
const thisDoesntCauseRerenders = useUntrackedCallback(() => console.log(signalA$.get()))
thisDoesntCauseRerenders()

useStateSignal

This is just if your fellow dev is using React paradigm while you are using signals. You use it to turn a useState or useMemo (or non-signal prop) into a signal.

const [state, setState] = useState(0)
const signal$ = useStateSignal(state, setState)

const memo = useMemo(() => state * 2, [state])
const computed$ = useStateSignal(memo)

const computed2$ = useStateSignal(props.example)

Component

Instead of "useStateSignal", you may use the "" component to inline-convert some props, like when you are mapping an array to components. This way you can easily pass all props you want as signals to be consistent.

import { f } from 'thenameisf'
import 'f/components/f-to-signals.js'

f(function myComponent ({ html }) {
  const users$ = useSignal(['Alice', 'Bob'])
  const fieldsToBeMergedToBelowProps = { user, test: 'Hi!' }

  return html`${users$.get().map(user => html`
    <f-to-signals key=${user} props=${{
      from: ['user'], // the user field will be turned into a user$ signal prop
      ...fieldsToBeMergedToBelowProps,
      render: props => { return html`<div>Hello ${props.user$.get()} - ${props.test}</div>` }
    }} />`
  `)}`
})

useSignalState

The inverse, to turn a signal into a state or a computed into a memo.

const signal$ = useSignal(3)
const [state /* 3 */, setState] = useSignalState(signal$)

const computed$ = useComputed(() => signal$.get() * 2)
const memo = useSignalState(computed$) // 6

Hot Reloading

There is no hot reloading support but you may use server-sent events and a file watcher to reload the web app on development on file changes.

You should set the flag globalThis._F_SHOULD_RESTORE_STATE_ON_TAB_RELOAD to true to remember hooks' state between tab reloads. It temporarily stores the state on window.sessionStorage.

Depending on the way you manage your app routes, after clicking some links different components may render on tab reload. To keep correct hook state, add a fixed id to your components like <a-component id='f7dcce56a4bcf' /><b-component id='62e7ff6526ba' />

See /example/server.js or read esbuild live reload docs if you need a build step. Also see how we set that global flag to true and listen to server-sent events at /example/index.html.

On reloads, useTask and useAsyncComputed have a isHotStart argument for the function they use. You can do if (isHotStart) return to avoid re-running these hooks.

Other hooks have an additional shouldCache config you may set to false to skip restore. For example: useSignal('test', { shouldCache: false })

Ad-hoc Usage

There are exports available for toSignal, toComputed and toStore for manual usage. They don't have the same options as their hook versions such as shouldCache.

Hooks are easier to use because they are automatically discarded when the component unmounts.

toTask and toAsyncComputed are missing for now.