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

@baystream/ui-core

v1.0.8

Published

Core React UI components and Syncfusion-based grid helpers used by Baystream applications.

Readme

@baystream/ui-core

Core React UI components and Syncfusion-based grid helpers used by Baystream applications.

Install

npm install @baystream/ui-core react react-dom

Verify published packages (monorepo)

This repository includes a Vite smoke app, library-testing. By default it is an npm workspace member and resolves @baystream/ui-core / @baystream/ui-theme to local packages/* after npm install at the repo root. You can temporarily switch that app to published semver installs to mimic consumers; see the app README. From the repo root: npm run dev:library-testing, npm run build:library-testing, and npm run typecheck:library-testing. Optional VITE_SYNCFUSION_LICENSE lives in library-testing/.env.

TypeScript and IntelliSense

Install from the npm registry (published tarball includes .js + .d.ts). Import only from the package entry — not from deep paths inside this repo:

import { FlexButton, type FlexButtonProps } from '@baystream/ui-core'

VS Code / TypeScript should then offer import completions, Ctrl+Space prop suggestions in JSX, and hover docs sourced from exported prop types (e.g. FlexButtonProps, FlexDataGridProps, FlexNavbarProps).

This package also relies on:

  • @baystream/ui-hooks
  • @baystream/ui-theme
  • @baystream/ui-utils
  • Syncfusion grid packages used internally by the components

Main exports

Components

  • FlexButton
  • FlexColumnConfigurationProvider
  • FlexDataGrid
  • FlexNavbar
  • ReduxColumnConfigurator
  • FlexSyncfusionTheme

Theme loading

  • loadFlexSyncfusionTheme
  • FLEX_SYNCFUSION_THEME_VARIANTS
  • FLEX_SYNCFUSION_THEME_CSS_STEM
  • FLEX_SYNCFUSION_ICONS_CSS_STEM

Syncfusion data helpers

  • DataManager
  • Query
  • UrlAdaptor

Grid services

  • Clipboard
  • ContextMenu
  • Filter
  • InfiniteScroll
  • Page
  • Resize
  • Search
  • Selection
  • Sort
  • Toolbar
  • VirtualScroll

Quick start

Load a Syncfusion theme once, then render Baystream components.

import { useEffect } from 'react'

import {
  FlexButton,
  FlexDataGrid,
  FlexNavbar,
  loadFlexSyncfusionTheme,
} from '@baystream/ui-core'
import { flexSpacing } from '@baystream/ui-theme'

type InvoiceRow = {
  id: number
  customer: string
  amount: number
  status: string
}

const rows: InvoiceRow[] = [
  { id: 1001, customer: 'Acme Ltd', amount: 1800, status: 'Paid' },
  { id: 1002, customer: 'Nova Retail', amount: 4200, status: 'Pending' },
]

export function BillingDashboard() {
  useEffect(() => {
    void loadFlexSyncfusionTheme('bootstrap5')
  }, [])

  return (
    <div style={{ padding: flexSpacing.lg }}>
      <FlexNavbar
        appName="Baystream Billing"
        rightMenus={[
          {
            id: 'invoices',
            label: 'Invoices',
            onClick: () => {
              /* e.g. navigate('/invoices') */
            },
          },
          {
            id: 'customers',
            label: 'Customers',
            onClick: () => {
              /* e.g. navigate('/customers') */
            },
          },
        ]}
        rightEmail="[email protected]"
        rightEmailMenuItems={[
          { label: 'User Management', onClick: () => console.log('users') },
          { label: 'Logout', onClick: () => console.log('logout') },
        ]}
      />

      <div style={{ marginTop: flexSpacing.lg, display: 'flex', gap: flexSpacing.sm }}>
        <FlexButton color="primary">Create invoice</FlexButton>
        <FlexButton variant="outline" color="secondary">
          Export
        </FlexButton>
      </div>

      <div style={{ marginTop: flexSpacing.lg }}>
        <FlexDataGrid<InvoiceRow>
          dataSource={rows}
          columns={[
            { field: 'id', headerText: 'Invoice ID', width: 120, isPrimaryKey: true },
            { field: 'customer', headerText: 'Customer', width: 220 },
            { field: 'amount', headerText: 'Amount', width: 140 },
            { field: 'status', headerText: 'Status', width: 140 },
          ]}
        />
      </div>
    </div>
  )
}

Theme setup

Syncfusion styles are global. Load one theme near app startup before rendering grid-based components.

Option 1: runtime loader

import { useEffect } from 'react'
import { loadFlexSyncfusionTheme } from '@baystream/ui-core'

export function AppThemeBoot() {
  useEffect(() => {
    void loadFlexSyncfusionTheme('bootstrap5')
  }, [])

  return null
}

Option 2: direct theme import

import '@baystream/ui-core/themes/bootstrap5'

Available theme variants:

  • material
  • materialDark
  • material3
  • material3Dark
  • bootstrap5
  • bootstrap5Dark
  • fluent2
  • fluent
  • fluentDark
  • fabric
  • fabricDark
  • tailwind3

FlexButton

Button component with semantic colors, variants, sizes, icons, and loading state.

Props

| Prop | Type | What it does | | --- | --- | --- | | variant | contained \| outline \| ghost | Selects the visual button style. | | color | semantic color name | Chooses the semantic action color. | | size | sm \| md \| lg | Controls padding and font scale. | | startIcon | ReactNode | Renders content before the label. | | endIcon | ReactNode | Renders content after the label. | | loading | boolean | Disables the button and shows a loading indicator. | | loadingIndicator | ReactNode | Replaces the default loading marker. | | loadingLabel | ReactNode | Replaces the visible label while loading. | | unstyled | boolean | Turns off built-in inline styles. | | backgroundColor | string | Per-instance background override. | | outlineColor | string | Per-instance border color override. | | borderRadius | CSS radius value | Per-instance border radius override. | | customStyles | CSSProperties | Merges a custom style object on top. |

  • variant: contained | outline | ghost
  • color: primary | secondary | success | warning | info | light | dark | error | danger
  • size: sm | md | lg
  • startIcon
  • endIcon
  • loading
  • loadingIndicator
  • loadingLabel
  • unstyled
  • backgroundColor
  • outlineColor
  • borderRadius
  • customStyles

Example: action bar

import { FlexButton } from '@baystream/ui-core'

export function ActionBar() {
  return (
    <div style={{ display: 'flex', gap: 12 }}>
      <FlexButton color="primary" variant="contained">
        Save
      </FlexButton>

      <FlexButton color="secondary" variant="outline">
        Cancel
      </FlexButton>

      <FlexButton color="danger" variant="ghost">
        Delete
      </FlexButton>
    </div>
  )
}

Example: loading state with icons

import { useState } from 'react'
import { FlexButton } from '@baystream/ui-core'

export function UploadButton() {
  const [loading, setLoading] = useState(false)

  async function handleUpload() {
    setLoading(true)
    try {
      await new Promise((resolve) => setTimeout(resolve, 1500))
    } finally {
      setLoading(false)
    }
  }

  return (
    <FlexButton
      color="primary"
      startIcon="^"
      endIcon=">"
      loading={loading}
      loadingLabel="Uploading..."
      loadingIndicator="o"
      onClick={handleUpload}
    >
      Upload file
    </FlexButton>
  )
}

FlexDataGrid

Declarative wrapper around Syncfusion Grid with Baystream defaults for sorting, menu filtering, toolbar search, selection, resize, reorder, clipboard, and context menus.

Props

| Prop | Type | What it does | | --- | --- | --- | | columns | column definition array | Declares visible grid columns and optional checkbox selection column. | | dataSource | row array or DataManager | Supports local arrays or remote/adaptor-backed data. | | allowSorting | boolean | Enables header sorting. | | allowFiltering | boolean | Enables column filter menus. | | allowSearching | boolean | Enables toolbar search behavior. | | allowSelection | boolean | Enables row selection behavior. | | allowColumnVisibilityToggle | boolean | Adds the Baystream header hide-column behavior. | | builtInColumnChooser | boolean | Adds Syncfusion's column chooser when visibility toggle is enabled. | | rowHeight | number | Controls row density in pixels. | | fontSize | CSS font size | Scales grid text size. | | headerFontSize | CSS font size | Column headers only; body cells keep following fontSize. | | headerHeight | CSS height | Header and filter-bar row cell height (applied via CSS). Suggested token: flexLayout.dataGridHeaderHeight from @baystream/ui-theme. | | injectServices | Syncfusion service array | Adds services such as Page, VirtualScroll, or InfiniteScroll. | | toolbar | toolbar item array | Controls Syncfusion toolbar actions such as Search. | | height | number or CSS size | Sets the rendered grid height. | | remoteLoadingIndicators | boolean | When true, skeleton until first dataBound, footer bar on later infiniteScroll fetches. Hides Syncfusion’s default grid spinner so loading UI is not duplicated. | | remoteLoadingSkeletonRows | number | Skeleton row count for the initial overlay (default 8). | | remoteLoadingMoreLabel | string | Footer text while loading the next block (default "Loading more…"). |

  • columns
  • dataSource
  • allowSorting
  • allowFiltering
  • allowSearching
  • allowSelection
  • allowColumnVisibilityToggle
  • builtInColumnChooser
  • rowHeight
  • fontSize
  • headerFontSize
  • headerHeight
  • injectServices

Example: local data grid

import { FlexDataGrid } from '@baystream/ui-core'
import { flexLayout } from '@baystream/ui-theme'

type ProductRow = {
  id: string
  name: string
  category: string
  price: number
  isSelected?: boolean
}

const rows: ProductRow[] = [
  { id: 'P-100', name: 'Keyboard', category: 'Hardware', price: 49 },
  { id: 'P-101', name: 'Mouse', category: 'Hardware', price: 25 },
  { id: 'P-102', name: 'Desk License', category: 'Software', price: 99 },
]

export function ProductGrid() {
  return (
    <FlexDataGrid<ProductRow>
      dataSource={rows}
      columns={[
        { type: 'checkbox', width: 56 },
        { field: 'id', headerText: 'ID', width: 110, isPrimaryKey: true },
        { field: 'name', headerText: 'Name', width: 220 },
        { field: 'category', headerText: 'Category', width: 160 },
        { field: 'price', headerText: 'Price', width: 120 },
      ]}
      allowSelection
      allowSearching
      allowSorting
      allowFiltering
      rowHeight={36}
      headerHeight={flexLayout.dataGridHeaderHeight}
      fontSize={14}
    />
  )
}

Example: column chooser and hide/show

import { FlexDataGrid } from '@baystream/ui-core'

type CustomerRow = {
  id: number
  name: string
  email: string
  region: string
}

const customers: CustomerRow[] = [
  { id: 1, name: 'Asha', email: '[email protected]', region: 'North' },
  { id: 2, name: 'Ravi', email: '[email protected]', region: 'South' },
]

export function CustomerGrid() {
  return (
    <FlexDataGrid<CustomerRow>
      dataSource={customers}
      columns={[
        { field: 'id', headerText: 'ID', isPrimaryKey: true, showInColumnChooser: false },
        { field: 'name', headerText: 'Name' },
        { field: 'email', headerText: 'Email', visible: false },
        { field: 'region', headerText: 'Region' },
      ]}
      allowColumnVisibilityToggle
      builtInColumnChooser
      toolbar={['Search']}
    />
  )
}

Example: remote data source

import { DataManager, UrlAdaptor, FlexDataGrid } from '@baystream/ui-core'

type ApiRow = {
  id: number
  name: string
}

const dataSource = new DataManager({
  url: '/api/users',
  adaptor: new UrlAdaptor(),
})

export function RemoteUsersGrid() {
  return (
    <FlexDataGrid<ApiRow>
      dataSource={dataSource}
      columns={[
        { field: 'id', headerText: 'ID', isPrimaryKey: true, width: 90 },
        { field: 'name', headerText: 'Name', width: 220 },
      ]}
    />
  )
}

Remote grids: adaptors, GET, and server-side sort

The snippet above assumes an API that matches Syncfusion’s default POST + JSON body UrlAdaptor behavior. Real REST endpoints often differ:

| Topic | What to know | | --- | --- | | GET + query string | Set adaptor.options.requestType = 'get' and override convertToQueryString so skip / take (and filters) become your query params (e.g. skip, limit). | | Response shape | The grid expects result + count. Map your JSON in processResponse. Base UrlAdaptor.processResponse may call JSON.parse(request.data) — inappropriate when the response body is in the fetch result, not request.data — so many apps subclass and map without calling that path. | | Server-side sorting | Column sorts issue new remote requests. Syncfusion accumulates sort descriptors in req[adaptor.options.sortBy] (default key sorted) as { name, direction }[]. Your adaptor must translate that into your API’s sort contract (sortBy / order, $orderby, sort=-field, POST fields, etc.). Sorting only “in the browser” happens with a local array dataSource or if sort params are never sent. | | Full demonstration | In this monorepo, library-testing is an end-to-end sample: remote DataManager, GET DummyJsonProductsAdaptor (pagination + server-side sort mapping), infinite scroll, dynamic columns from dynamicProductColumns.ts, Redux column configuration, and optional remoteLoadingIndicators (skeleton + batch loader). Run npm run dev:library-testing from the repo root. |

Example: virtualization

import { FlexDataGrid, VirtualScroll } from '@baystream/ui-core'

const rows = Array.from({ length: 2000 }, (_, index) => ({
  id: index + 1,
  name: `Item ${index + 1}`,
}))

export function VirtualizedGrid() {
  return (
    <FlexDataGrid<{ id: number; name: string }>
      dataSource={rows}
      columns={[
        { field: 'id', headerText: 'ID', isPrimaryKey: true, width: 90 },
        { field: 'name', headerText: 'Name', width: 220 },
      ]}
      height={420}
      enableVirtualization
      injectServices={[VirtualScroll]}
    />
  )
}

FlexNavbar

Top navigation bar with brand area, optional right-side actions (onClick only), and optional user email dropdown.

Props

| Prop | Type | What it does | | --- | --- | --- | | appName | string | Primary brand text shown in the navbar. | | variant | withRightMenus \| withoutRightMenus | Toggles whether right-side menus are expected. | | sticky | boolean | Keeps the navbar pinned to the top when true. | | logo | ReactNode | Replaces the default logo image with custom content. | | logoSrc | string | Replaces the default logo image source. | | logoAlt | string | Sets the logo image alt text. | | logoTextFontSize | CSS font size | Overrides brand text size. | | onBrandClick | () => void | Handles brand click action. | | rightMenus | { id, label, onClick }[] | Renders right-side actions as buttons; host uses onClick for routing or anything else (no href). | | rightEmail | string | Shows the signed-in email text or menu trigger. | | rightEmailMenuItems | { label \| text, onClick, ... }[] | Account dropdown items; each row is a button and uses onClick. | | rightSlot | ReactNode | Adds custom content at the far right. | | appBarProps | app bar props | Passes through lower-level app bar customization. |

  • appName
  • variant
  • sticky
  • logo
  • logoSrc
  • logoAlt
  • logoTextFontSize
  • onBrandClick
  • rightMenus
  • rightEmail
  • rightEmailMenuItems
  • rightSlot

Example: standard app header

import { FlexNavbar } from '@baystream/ui-core'

export function AppHeader() {
  return (
    <FlexNavbar
      appName="Baystream Admin"
      rightMenus={[
        {
          id: 'dashboard',
          label: 'Dashboard',
          onClick: () => {
            /* navigate('/dashboard') or your router */
          },
        },
        {
          id: 'reports',
          label: 'Reports',
          onClick: () => {
            /* navigate('/reports') */
          },
        },
      ]}
      rightEmail="[email protected]"
      rightEmailMenuItems={[
        { label: 'User Management', onClick: () => console.log('users') },
        { label: 'Logout', onClick: () => console.log('logout') },
      ]}
    />
  )
}

Example: custom brand and no right links

import { FlexNavbar } from '@baystream/ui-core'

export function FormsHeader() {
  return (
    <FlexNavbar
      appName="FlexForms"
      variant="withoutRightMenus"
      logo={<span aria-hidden>BF</span>}
      rightEmail="[email protected]"
      rightEmailMenuItems={[
        { label: 'Logout', onClick: () => console.log('logout') },
      ]}
    />
  )
}

Data helpers

DataManager, Query, and UrlAdaptor are re-exported so consumers can build remote grid integrations without importing directly from @syncfusion/ej2-data.

import { DataManager, Query, UrlAdaptor } from '@baystream/ui-core'

const manager = new DataManager({
  url: '/api/orders',
  adaptor: new UrlAdaptor(),
})

const query = new Query().take(25)
void manager
void query

Column configuration

Use FlexColumnConfigurationProvider, useFlexColumnConfiguration, ReduxColumnConfigurator, and useSyncfusionGridColumnVisibility when you want persisted column visibility/order with FlexDataGrid and no Syncfusion Column Chooser toolbar.

The default path is plug-and-play: ui-core owns a small Redux store and persists it to localStorage.

import {
  FlexColumnConfigurationProvider,
  FlexDataGrid,
  ReduxColumnConfigurator,
  useFlexColumnConfiguration,
  useSyncfusionGridColumnVisibility,
  type FlexGridHandle,
} from '@baystream/ui-core'
import { useRef } from 'react'

function OrdersGridInner() {
  const gridRef = useRef<FlexGridHandle | null>(null)
  const columnConfiguration = useFlexColumnConfiguration({
    columns: ORDER_COLUMNS,
    storageKey: 'orders-grid-columns',
    defaultVisibleFields: ['id', 'customer', 'amount'],
  })

  useSyncfusionGridColumnVisibility(
    gridRef,
    columnConfiguration.columnConfigs,
    true,
  )

  return (
    <>
      <ReduxColumnConfigurator
        columnConfigs={columnConfiguration.columnConfigs}
        defaultColumnConfigs={columnConfiguration.defaultColumnConfigs}
        onApply={columnConfiguration.applyColumnConfigs}
      />
      <FlexDataGrid
        ref={gridRef}
        columns={columnConfiguration.mergedColumns}
        dataSource={orders}
        allowColumnVisibilityToggle
        allowReordering
        columnVisibilityChanged={columnConfiguration.columnVisibilityChanged}
        actionComplete={columnConfiguration.actionComplete}
      />
    </>
  )
}

export function OrdersGrid() {
  return (
    <FlexColumnConfigurationProvider>
      <OrdersGridInner />
    </FlexColumnConfigurationProvider>
  )
}

Use storage="memory" for non-persistent demos/tests, or localStorageKey when you want to namespace the browser storage key.

Advanced: bind to an app Redux store

If a host app wants this state in its own Redux tree, mount columnConfigurationReducer under a stable key and bind the hook with createUseColumnConfiguration:

import { combineReducers } from '@reduxjs/toolkit'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'

import { columnConfigurationReducer } from '@baystream/ui-core'

const persistedReducerInner = combineReducers({
  columnConfiguration: columnConfigurationReducer,
})

export const persistedReducer = persistReducer(
  { key: 'persisted', storage, whitelist: ['columnConfiguration'] },
  persistedReducerInner,
)

Bind the column hook to your store

import { createUseColumnConfiguration } from '@baystream/ui-core'
import { useDispatch, useSelector } from 'react-redux'

import type { AppDispatch, RootState } from './redux/store'

export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
export const useAppSelector = useSelector.withTypes<RootState>()

/** True once redux-persist has rehydrated, or always `true` if you do not use persist */
export function selectPersistedRehydrated(state: RootState): boolean {
  const pe = state.persisted._persist
  return pe == null ? true : pe.rehydrated === true
}

export const useColumnConfiguration = createUseColumnConfiguration<
  RootState,
  AppDispatch
>({
  useAppDispatch,
  useAppSelector,
  selectColumnConfigurationState: (s) => s.persisted.columnConfiguration,
  selectPersistedRehydrated,
})

Then use your bound useColumnConfiguration, ReduxColumnConfigurator, and useSyncfusionGridColumnVisibility the same way the plug-and-play example uses useFlexColumnConfiguration.

Notes

  • Syncfusion theme CSS is global. In production, pick one theme per page load.
  • For large datasets, combine enableVirtualization with VirtualScroll.
  • For internal Baystream apps, prefer these wrappers over importing Syncfusion grid pieces directly unless you need behavior not exposed here.