🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@rocknblock/frontend-toolkit

Package Overview
Dependencies
Maintainers
2
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@rocknblock/frontend-toolkit

Reusable React frontend hooks and utilities.

latest
npmnpm
Version
1.0.1
Version published
Weekly downloads
4
-69.23%
Maintainers
2
Weekly downloads
 
Created
Source

@rocknblock/frontend-toolkit

Reusable React hooks and frontend utility functions.

Installation

npm i @rocknblock/frontend-toolkit

Peer dependency:

  • react >= 18

Quick Start

import {
  useCountdown,
  useResource,
  sleep,
  mergeDeep,
  createRouter,
  type RouteParamValue,
} from '@rocknblock/frontend-toolkit';

Exports

The package exports everything from src/hooks/* and src/utils/* via a single entrypoint.

Hooks

  • useAwaiting
  • useBeforeUnload
  • useControls
  • useCountdown
  • useDebounceCallback
  • useDebouncedValue
  • useDeepEffect
  • useDeepMemo
  • useFormattedTokenAmount
  • useInterval
  • useLocalStorage
  • useMedia
  • useMergeRefs
  • useMountedState, useMounted (alias)
  • useResource
  • useSet
  • useShallowSelector
  • useValidateInput
  • useValueRef

Utils

  • getDevice, openWindow
  • camelize, decamelize
  • swap, toArray, hasIntersections, mapSet, removeBy, chunkCalls, paginated
  • noop, sleep, divide, capitalize, prepend, truncateHash, shortenPhrase
  • extractErrorMsg
  • normalizeDecimalValue, validateDecimal, formatDate, numberFormatter, formatTokenAmount, formatThousands, percent, formatNumber
  • curry, retry
  • pick, shallowEqual, isEmptyRecord, cloneDeep, mergeDeep, dataAttr
  • replaceParams, toParams, params, createRouter, validateQuery

Types

  • DeviceType
  • RouteParamValue
  • PaginationParams
  • ValidateInputConfig
  • ValidateInputResult
  • ResourceState
  • UseResourceConfig
  • UseResourceResult

Development

npm run typecheck
npm run lint
npm run build

Other scripts:

  • npm run format
  • npm run lint:fix
  • npm run release

Build output: dist/.

Usage Examples

useResource: manual fetch + abort + optimistic local patch

import { useEffect, useState } from 'react';
import { useResource } from '@rocknblock/frontend-toolkit';

type User = { id: string; name: string; role: string };

export function UserPanel({ userId }: { userId: string }) {
  const [enabled, setEnabled] = useState(false);

  const { data, loading, error, refetch, setData, abort } = useResource<User, string>(userId, {
    enabled,
    immediate: false,
    keepPreviousData: true,
    abortable: true,
    fetcher: async (id, signal) => {
      const res = await fetch(`/api/users/${id}`, { signal });
      if (!res.ok) throw new Error('Failed to load user');
      return (await res.json()) as User;
    },
    onError: (e) => console.error(e),
  });

  useEffect(() => {
    setEnabled(true);
    void refetch();
    return () => abort();
  }, [refetch, abort]);

  const promote = () => setData((prev) => (prev ? { ...prev, role: 'admin' } : prev));

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Failed to load</div>;

  return (
    <div>
      <div>{data?.name}</div>
      <div>{data?.role}</div>
      <button onClick={() => void refetch()}>Reload</button>
      <button onClick={promote}>Promote locally</button>
    </div>
  );
}

useShallowSelector: avoid unstable object identity in selected slice

import { useMemo } from 'react';
import { useShallowSelector } from '@rocknblock/frontend-toolkit';

type DashboardState = {
  users: { online: number; total: number };
  ui: { theme: string; sidebarOpen: boolean };
};

export function HeaderStats({ state }: { state: DashboardState }) {
  const stats = useShallowSelector(state, (s) => ({
    online: s.users.online,
    total: s.users.total,
  }));

  const label = useMemo(() => `${stats.online}/${stats.total} online`, [stats]);
  return <span>{label}</span>;
}

useDeepEffect: run effect only on real deep changes in deps object

import { useState } from 'react';
import { useDeepEffect } from '@rocknblock/frontend-toolkit';

type Filters = {
  tags: string[];
  range: { from: string; to: string };
};

export function SearchPage() {
  const [filters, setFilters] = useState<Filters>({
    tags: ['react'],
    range: { from: '2026-01-01', to: '2026-12-31' },
  });

  useDeepEffect(() => {
    const query = new URLSearchParams({
      tags: filters.tags.join(','),
      from: filters.range.from,
      to: filters.range.to,
    });
    void fetch(`/api/search?${query}`);
  }, [filters]);

  return <button onClick={() => setFilters((p) => ({ ...p }))}>Recreate same object</button>;
}

useMergeRefs: combine forwarded ref with internal ref

import { forwardRef, useEffect, useRef } from 'react';
import { useMergeRefs } from '@rocknblock/frontend-toolkit';

type Props = { autoFocus?: boolean };

export const SearchInput = forwardRef<HTMLInputElement, Props>(function SearchInput(
  { autoFocus = false },
  forwardedRef,
) {
  const localRef = useRef<HTMLInputElement>(null);
  const ref = useMergeRefs<HTMLInputElement>(forwardedRef, localRef);

  useEffect(() => {
    if (autoFocus) localRef.current?.focus();
  }, [autoFocus]);

  return <input ref={ref} placeholder="Search" />;
});

useAwaiting: one loading flag for many concurrent async tasks

import { useAwaiting } from '@rocknblock/frontend-toolkit';

export function SaveButtons() {
  const { loading, wrap } = useAwaiting();

  const saveProfile = () =>
    wrap(async () => {
      await fetch('/api/profile', { method: 'POST' });
    });

  const saveSettings = () =>
    wrap(async () => {
      await fetch('/api/settings', { method: 'POST' });
    });

  return (
    <div>
      <button onClick={saveProfile} disabled={loading}>
        Save profile
      </button>
      <button onClick={saveSettings} disabled={loading}>
        Save settings
      </button>
      {loading && <span>Saving...</span>}
    </div>
  );
}

useValidateInput: normalize + validate + explicit reset

import { useValidateInput } from '@rocknblock/frontend-toolkit';

export function AmountField() {
  const amount = useValidateInput('', {
    normalize: (v) => v.replace(',', '.').trim(),
    validate: (v) => /^\d+(\.\d{1,2})?$/.test(v),
    errorMessage: 'Enter a valid amount with up to 2 decimals',
  });

  return (
    <div>
      <input value={amount.value} onChange={(e) => amount.onChange(e.target.value)} />
      {!amount.isValid && amount.touched && <div>{amount.error}</div>}
      <button onClick={() => amount.reset('0')}>Reset to 0</button>
    </div>
  );
}

createRouter + toParams

import { createRouter } from '@rocknblock/frontend-toolkit';

const router = createRouter('/app');
const url = router.query('/users/:id', { id: '42' }, { tab: 'profile', filter: ['a', 'b'] });
// /app/users/42?tab=profile&filter=a&filter=b

mergeDeep

import { mergeDeep } from '@rocknblock/frontend-toolkit';

const config = mergeDeep({ api: { retries: 1, timeout: 1000 } }, { api: { timeout: 3000 } });
// { api: { retries: 1, timeout: 3000 } }

FAQs

Package last updated on 11 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts