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

nuqisukuk-widgets

v1.0.0

Published

Embeddable React / Next.js widgets for Nuqi Sukuk — browse Shariah-compliant sukuk and view instrument detail

Readme

nuqisukuk-widgets

Embeddable React / Next.js widgets for Nuqi Sukuk — drop a live catalog of Shariah-compliant sukuk and a full instrument detail panel into any host app in minutes.

| Widget | Feature key | API endpoint | |---|---|---| | <SukukList> | sukuk-list | GET /sdk/sukuk | | <SukukDetails> | sukuk-details | GET /sdk/sukuk/:id |


Requirements

  • React 18 or 19 (peer dependency — the host app provides it)
  • Node ≥ 18
  • An apiKey + clientId from the Nuqi Sukuk platform

Install

npm install nuqisukuk-widgets

Quick start

import {
  NuqiSukukProvider,
  SukukList,
  SukukDetails,
} from 'nuqisukuk-widgets';
import 'nuqisukuk-widgets/styles.css';   // import once per app

function App() {
  const [selectedId, setSelectedId] = useState('');

  return (
    <NuqiSukukProvider
      apiKey="nsk_live_..."
      clientId="client_..."
      baseUrl="https://uat-api.nuqisukuk.com"
      theme="emerald"
    >
      <SukukList onSelect={(s) => setSelectedId(s.id)} />
      {selectedId && <SukukDetails sukukId={selectedId} />}
    </NuqiSukukProvider>
  );
}

The provider calls GET /sdk/config once on mount. The response lists which feature keys are unlocked for the client. A widget whose key is missing renders a locked placeholder and never makes a data request.


API reference

<NuqiSukukProvider>

Wrap your widget tree with this once. All children share the same auth context.

| Prop | Type | Default | Description | |---|---|---|---| | apiKey | string | required | Publishable key — sent as x-api-key on every request | | clientId | string | required | Client identifier — sent as x-client-id; selects the feature plan | | userToken | string | — | Optional JWT for per-user feature gating — sent as Authorization: Bearer | | baseUrl | string | https://uat-api.nuqisukuk.com | API origin | | theme | ThemeProp | — | Preset name or ThemeConfig object (see Theming) | | refreshInterval | number (ms) | 60000 | Auto-poll interval for data widgets; 0 disables polling | | locale | string | en-US | BCP-47 locale for number and date formatting | | onError | (err: NuqiSukukError) => void | — | Called on any network or config error from a child widget | | className | string | — | Extra class on the provider wrapper <div> | | style | React.CSSProperties | — | Extra inline styles on the provider wrapper |


<SukukList>

Catalog of available sukuk. Cards switch from a single-column to two-column grid based on the container width (≥ 560 px triggers two columns), so it re-flows inside any sidebar or column without breakpoint overrides.

| Prop | Type | Default | Description | |---|---|---|---| | title | string \| null | "Available Sukuk" | Widget heading; pass null to hide | | limit | number | 20 | Maximum number of sukuk to request (capped server-side) | | sector | string | — | Filter by sector | | search | string | — | Free-text search across name and issuer | | showComingSoon | boolean | true | Include sukuk flagged as coming soon | | onSelect | (sukuk: SukukSummary) => void | — | Fired when a card is clicked; pass this to make cards interactive | | theme | ThemeProp | — | Per-widget theme override (wins over the provider theme) | | className | string | — | Extra class on the widget card | | style | React.CSSProperties | — | Extra inline styles on the widget card |

Wiring list → detail:

const [id, setId] = useState('');

<SukukList onSelect={(s) => setId(s.id)} />
{id && <SukukDetails sukukId={id} />}

<SukukDetails>

Full instrument detail for a single sukuk. Fetches /sdk/sukuk/:id and renders issuer info, profit & yield terms, pricing, and listing data. The four column groups collapse to 3 → 2 → 1 columns as the container narrows (breakpoints at 900 / 640 / 420 px).

| Prop | Type | Default | Description | |---|---|---|---| | sukukId | string | required | The id field from a SukukSummary | | title | string \| null | "Sukuk Details" | Heading shown while data loads; replaced by the instrument name once loaded | | theme | ThemeProp | — | Per-widget theme override | | className | string | — | Extra class on the widget card | | style | React.CSSProperties | — | Extra inline styles on the widget card |


Theming

theme accepts a preset name or a full ThemeConfig object. Both can be set on the provider (cascades to all widgets) or on an individual widget (overrides the provider theme for that widget only).

Presets

| Name | Look | |---|---| | emerald | Deep near-black surface with Shariah-green accent (default) | | sand | Charcoal surface with warm gold accent | | dark | Neutral dark, no green tint | | light | White card, light-mode | | midnight | Deep navy surface with mint accent |

<NuqiSukukProvider theme="midnight" ...>

Custom ThemeConfig

Pass a partial object — every field is optional and falls back to the CSS defaults.

<NuqiSukukProvider
  theme={{
    accent:        '#7c5cff',
    accentText:    '#ffffff',
    profit:        '#a78bfa',
    up:            '#4ade80',
    down:          '#f87171',
    cardBg:        'rgba(17,14,33,0.94)',
    cardBorder:    'rgba(124,92,255,0.18)',
    cardRadius:    '20px',
    cardShadow:    '0 12px 36px rgba(0,0,0,0.6)',
    surface:       'rgba(255,255,255,0.045)',
    textPrimary:   '#f3f0ff',
    textSecondary: '#c4b8ff',
    textMuted:     '#7c6fa8',
    badgeBg:       'rgba(124,92,255,0.14)',
    badgeText:     '#b8a6ff',
  }}
  ...
>

ThemeConfig fields

| Field | CSS variable | Purpose | |---|---|---| | accent | --ns-accent | Active toggles, focus rings, links | | accentText | --ns-accent-text | Text drawn on top of an accent fill | | profit | --ns-profit | Profit-rate / yield highlight | | up | --ns-up | Positive change colour | | down | --ns-down | Negative change colour | | cardBg | --ns-card-bg | Card background | | cardBorder | --ns-card-border | Card border | | cardRadius | --ns-card-radius | Card corner radius | | cardShadow | --ns-card-shadow | Card box-shadow | | surface | --ns-surface | Inner surface (skeleton blocks, list rows) | | textPrimary | --ns-text-primary | Primary text | | textSecondary | --ns-text-secondary | Secondary / supporting text | | textMuted | --ns-text-muted | Muted / label text | | badgeBg | --ns-badge-bg | Rating / sector badge background | | badgeText | --ns-badge-text | Rating / sector badge text |

You can also set these CSS variables directly on any ancestor element if you prefer a pure-CSS theming approach.


Error handling

<NuqiSukukProvider
  onError={(err) => {
    console.error(err.code, err.message, err.feature);
    // err.code: 'CONFIG_FETCH_FAILED' | 'NETWORK_ERROR' | 'FETCH_ERROR'
    // err.feature: 'sukuk-list' | 'sukuk-details' | undefined
  }}
  ...
>

Widgets also show an inline Retry button when a data fetch fails so users can recover without reloading the page.


Feature gating

Features are unlocked per client on the backend. When GET /sdk/config is called, the response includes the features array. Any widget whose key is not in that list renders a locked placeholder instead of fetching data — no extra code needed on the host.

To gate other UI in the host app on the same feature set:

import { useFeature } from 'nuqisukuk-widgets';

function InvestButton() {
  const canSeeSukuk = useFeature('sukuk-list');
  if (!canSeeSukuk) return null;
  return <button>Invest now</button>;
}

To read the full context:

import { useNuqiSukuk } from 'nuqisukuk-widgets';

const { features, isReady, isLoading, locale } = useNuqiSukuk();

Building a custom widget with useSdkFetch

import { useSdkFetch } from 'nuqisukuk-widgets';

function MySukukPriceBar({ sukukId }: { sukukId: string }) {
  const { data, loading, error, refetch } = useSdkFetch<{ askPrice: number }>(
    `/sdk/sukuk/${sukukId}/price`,
    { feature: 'sukuk-details', poll: true },
  );

  if (loading) return <Spinner />;
  if (error)   return <button onClick={refetch}>Retry</button>;
  return <span>{data?.askPrice}</span>;
}

useSdkFetch automatically:

  • Attaches x-api-key, x-client-id, and Authorization headers from the provider context
  • Polls on the provider refreshInterval
  • Aborts the in-flight request on unmount
  • Surfaces errors through onError and the returned error string

| Option | Type | Default | Description | |---|---|---|---| | params | Record<string, string> | — | Query string params | | feature | FeatureKey | — | Reported in onError.feature | | skip | boolean | false | Skip the request (e.g. widget locked) | | poll | boolean | true | Enable auto-polling on refreshInterval |


Next.js usage

The package is an ESM + CJS dual build with no "use client" directive — add it yourself in the file that imports the widgets:

// app/sukuk/page.tsx  (App Router)
'use client';

import {
  NuqiSukukProvider,
  SukukList,
  SukukDetails,
} from 'nuqisukuk-widgets';
import 'nuqisukuk-widgets/styles.css';

For Pages Router nothing special is needed.


TypeScript types exported

// Provider / context
NuqiSukukProviderProps
NuqiSukukConfig
NuqiSukukContextValue
NuqiSukukError
SdkConfigResponse
FeatureKey                  // 'sukuk-list' | 'sukuk-details'

// Widgets
SukukListProps
SukukDetailsProps

// Data shapes
SukukSummary                // lightweight — used in SukukList cards
SukukDetail                 // full detail — used in SukukDetails

// Theme
ThemeConfig
ThemeProp                   // string preset name | ThemeConfig
PresetThemeName             // 'emerald' | 'sand' | 'dark' | 'light' | 'midnight'

Preview app

frontend/ is a Vite dev app that renders both widgets against a local or UAT backend:

npm install && npm run build          # build the package first
cd frontend && npm install && npm run dev

Set API_KEY, CLIENT_ID, and BASE_URL at the top of frontend/src/App.tsx.