
Security News
Feross on TBPN: Socket's Series C and the State of Software Supply Chain Security
Feross Aboukhadijeh joins TBPN to discuss Socket's $60M Series C, 500%+ ARR growth, AI's impact on open source, and the rise in supply chain attacks.
ebig-library
Advanced tools
A modern, lightweight React + TypeScript UI component library by eBig — 35+ ready-to-use components, a responsive layout system, design-token theming, built-in i18n (en/vi), and optional eBig backend integration for dynamic form/table/page rendering.
npm install ebig-library
Import the CSS style files (required for layout and typography utilities):
<!-- In your index.html or root CSS -->
<link rel="stylesheet" href="https://cdn.ebig.co/library/style/root.min.css" />
<link rel="stylesheet" href="https://cdn.ebig.co/library/style/layout.min.css" />
<link rel="stylesheet" href="https://cdn.ebig.co/library/style/typography.min.css" />
<link rel="stylesheet" href="https://cdn.ebig.co/library/style/toast-noti.min.css" />
These are loaded automatically if you use
EbigProviderwithloadResources={true}(default).
Wrap your app's root with EbigProvider. It sets up routing (BrowserRouter internally), toast notifications, dialogs, token refresh, and optional design token + i18n loading from the eBig backend.
// main.tsx
import ReactDOM from 'react-dom/client'
import EbigProvider, { Route } from 'ebig-library'
ReactDOM.createRoot(document.getElementById('root')!).render(
<EbigProvider
pid="your-project-id"
url="https://your-ebig-api.com/"
fileUrl="https://your-file-server.com/"
imgUrlId="https://your-cdn.com/"
theme="light" // "light" | "dark"
loadResources={true} // false → skip backend token/i18n loading
>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
</EbigProvider>
)
Important:
EbigProviderwrapsBrowserRouterinternally. Do not add anotherBrowserRouterin your app.
| Prop | Type | Required | Description |
|---|---|---|---|
pid | string | ✅ | eBig project ID (32-char) |
url | string | ✅ | eBig API base URL |
fileUrl | string | ✅ | File server URL |
imgUrlId | string | ✅ | CDN/image URL prefix |
theme | "light" | "dark" | — | Default "light" |
loadResources | boolean | — | Default true. Set to false to skip backend loading |
onInvalidToken | () => void | — | Called on 401 — override to redirect to login |
children | ReactNode | — | <Route> elements |
import { useEbigContext } from 'ebig-library'
function MyComponent() {
const {
theme, // "light" | "dark"
setTheme,
userData, // current user object (set by you)
setUserData,
globalData, // any global state bag
setGlobalData,
i18n, // i18next instance
} = useEbigContext()
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle theme
</button>
)
}
Use row and col CSS classes for flex layouts. Add remain to let a child fill remaining space.
// Horizontal (flex-direction: row, align-items: center)
<div className="row" style={{ gap: 12 }}>
<span>Left</span>
<span className="remain">Stretches to fill space</span>
<span>Right</span>
</div>
// Vertical (flex-direction: column)
<div className="col" style={{ gap: 8 }}>
<span>Top</span>
<span>Bottom</span>
</div>
| Resource | URL |
|---|---|
| Design tokens (CSS variables) | https://cdn.ebig.co/library/style/root.min.css |
Layout utilities (row, col, grids) | https://cdn.ebig.co/library/style/layout.min.css |
Typography (heading-1…heading-8, body-1…body-3) | https://cdn.ebig.co/library/style/typography.min.css |
| Toast styles | https://cdn.ebig.co/library/style/toast-noti.min.css |
import { Button, SimpleButton, Ebigicon } from 'ebig-library'
// Basic
<Button label="Click me" onClick={() => alert('clicked!')} />
// Size + color variant
<Button label="Save" className="size40 button-primary" onClick={handleSave} />
// With prefix icon
<Button
label="Upload"
prefix={<Ebigicon src="outline/arrows/cloud-upload" size={16} />}
className="size40 button-neutral"
onClick={handleUpload}
/>
// As a link
<Button label="Docs" linkTo="https://ebig.co" target="_blank" className="size40 button-grey" />
// With tooltip
<Button label="Info" tooltip={{ message: 'More details', position: 'top' }} />
// Submit — also triggers on Enter key
<Button label="Submit" type="submit" className="size40 button-primary" onClick={handleSubmit} />
// SimpleButton — auto-disables itself during async onClick to prevent double-clicks
<SimpleButton label="Save" className="size40 button-primary" onClick={async () => { await save() }} />
Size classes: size24 · size32 (default) · size40 · size48 · size56 · size64
Color classes: button-primary · button-grey · button-neutral · button-black · button-white · button-infor · button-warning · button-error · button-success · button-infor-main · button-warning-main · button-error-main · button-success-main
SVG icon component. Icons are fetched from https://cdn.ebig.co/icon-library/ and cached in the browser via the Cache API after first load.
import { Ebigicon } from 'ebig-library'
<Ebigicon src="outline/user/user" size={24} />
<Ebigicon src="fill/actions/trash" size={20} color="#E14337" onClick={handleDelete} className="icon-button" />
// With tooltip
<Ebigicon src="outline/essential/info-circle" size={20} tooltip={{ message: 'Help', position: 'bottom' }} />
// From a custom link
<Ebigicon link="https://your-cdn.com/custom-icon.svg" size={24} />
Style classes: icon-button (adds hover/active styles) · icon-button light · border · dashed
Size classes: size24 · size32 · size40 · size48 · size56 · size64
import { TextField, Ebigicon } from 'ebig-library'
<TextField placeholder="Enter name" onChange={(e) => setName(e.target.value)} />
// With prefix icon and helper text (shown in red below)
<TextField
prefix={<Ebigicon src="outline/user/user" size={16} />}
placeholder="Email"
type="email"
helperText="Invalid email"
className="size40 body-3"
/>
// With react-hook-form
const { register } = useForm()
<TextField placeholder="Username" register={register('username', { required: true })} />
Size classes: size24 · size32 · size40 (default) · size48
import { TextArea } from 'ebig-library'
<TextArea placeholder="Write something..." onChange={(e) => setText(e.target.value)} />
Single-value dropdown. Supports static options, async lazy-loading, hierarchical (parent/child) options, and react-hook-form.
import { SelectDropdown } from 'ebig-library'
const options = [
{ id: '1', name: 'Option A' },
{ id: '2', name: 'Option B' },
{ id: '3', name: 'Option C', disabled: true },
]
// Static
<SelectDropdown
value={selected}
options={options}
placeholder="Choose one"
onChange={(item) => setSelected(item?.id)}
/>
// Async (loads on open / search)
<SelectDropdown
options={[]}
getOptions={async ({ length, search }) => {
const res = await fetchItems({ page: Math.floor(length / 10) + 1, search })
return { data: res.items, totalCount: res.total }
}}
onChange={(item) => setSelected(item?.id)}
/>
import { Checkbox } from 'ebig-library'
<Checkbox value={isChecked} onChange={(val) => setIsChecked(val)} />
<Checkbox value={null} /> {/* null = indeterminate */}
import { Switch } from 'ebig-library'
<Switch value={isOn} onChange={(val) => setIsOn(val)} />
<Switch value={isOn} size="2.4rem" onBackground="#287CF0" onChange={setIsOn} />
import { RadioButton } from 'ebig-library'
<RadioButton value={selected === 'a'} onChange={() => setSelected('a')} label="Option A" />
<RadioButton value={selected === 'b'} onChange={() => setSelected('b')} label="Option B" />
import { DateTimePicker } from 'ebig-library'
<DateTimePicker value={date} onChange={(val) => setDate(val)} placeholder="Pick a date" />
import { NumberPicker } from 'ebig-library'
<NumberPicker value={count} onChange={(val) => setCount(val)} min={0} max={100} />
import { Slider } from 'ebig-library'
<Slider value={volume} min={0} max={100} onChange={(val) => setVolume(val)} />
import { Rating } from 'ebig-library'
<Rating value={3} onChange={(val) => setRating(val)} />
import { InputOtp } from 'ebig-library'
<InputOtp length={6} onComplete={(code) => verifyOtp(code)} />
import { ColorPicker } from 'ebig-library'
<ColorPicker value={color} onChange={(hex) => setColor(hex)} />
import { Tag } from 'ebig-library'
<Tag label="Active" color="#287CF0" />
<Tag label="Error" color="#E14337" onRemove={() => handleRemove()} />
import { Pagination } from 'ebig-library'
<Pagination
pageIndex={page}
totalCount={total}
pageSize={10}
onChange={(newPage) => setPage(newPage)}
/>
import { InfiniteScroll } from 'ebig-library'
<InfiniteScroll
data={items}
render={(item) => <div key={item.id}>{item.name}</div>}
totalCount={total}
onLoadMore={() => loadNextPage()}
/>
import { showDialog, DialogAlignment } from 'ebig-library'
showDialog({
title: 'Confirm delete',
content: 'Are you sure?',
alignment: DialogAlignment.center,
onConfirm: () => handleDelete(),
})
import { Popup, showPopup, closePopup } from 'ebig-library'
import { useRef } from 'react'
const ref = useRef()
<button onClick={(ev) => showPopup({ ref, id: 'my-popup', clickTarget: ev.currentTarget, content: () => <div>Content</div> })}>
Open popup
</button>
<Popup ref={ref} />
import { ToastMessage } from 'ebig-library'
ToastMessage.success('Saved successfully')
ToastMessage.errors('Something went wrong')
ToastMessage.warning('Check your input')
import { ProgressBar } from 'ebig-library'
<ProgressBar percent={75} label="Uploading..." />
import { ProgressCircle } from 'ebig-library'
<ProgressCircle percent={60} size={80} />
import { Calendar } from 'ebig-library'
<Calendar value={date} onChange={(val) => setDate(val)} />
import { Carousel } from 'ebig-library'
<Carousel>
<img src="/slide1.jpg" alt="Slide 1" />
<img src="/slide2.jpg" alt="Slide 2" />
</Carousel>
import { VideoPlayer, AudioPlayer, IframePlayer } from 'ebig-library'
<VideoPlayer src="https://example.com/video.mp4" />
<AudioPlayer src="https://example.com/audio.mp3" />
<IframePlayer src="https://www.youtube.com/embed/xxxxx" />
import { ImportFile, UploadFiles } from 'ebig-library'
// File input with preview
<ImportFile
value={files}
onChange={(fileList) => setFiles(fileList)}
maxSize={5 * 1024 * 1024} // 5 MB
accept={['.png', '.jpg', '.pdf']}
/>
// Full upload flow with progress and CDN upload
<UploadFiles
value={uploadedFiles}
onChange={(files) => setUploadedFiles(files)}
/>
Rich-text editor powered by CKEditor 5. Requires ckeditor5 and @ckeditor/ckeditor5-react to be installed by the consumer.
import { CustomCkEditor5, CkEditorUploadAdapter } from 'ebig-library'
<CustomCkEditor5
value={html}
onChange={(val) => setHtml(val)}
uploadAdapter={CkEditorUploadAdapter}
/>
Lightweight rich-text editor (no CKEditor dependency). Supports emoji, bold, italic, underline, hyperlinks, and @mention-style suggestion hooks.
import { EbigEditor } from 'ebig-library'
<EbigEditor
placeholder="Write a comment..."
onChange={(value, el) => setContent(value)}
onBlur={(value, el) => handleBlur(value)}
/>
// Custom toolbar subset
<EbigEditor
customToolbar={['bold', 'italic', 'emoji']}
onChange={(val) => setContent(val)}
/>
// Mention suggestions
<EbigEditor
onSuggest={[{
triggerPattern: '@',
render: (offset, match, select) => (
<MentionList offset={offset} query={match} onSelect={select} />
)
}]}
onChange={(val) => setContent(val)}
/>
import { IconPicker } from 'ebig-library'
<IconPicker value={iconName} onChange={(name) => setIconName(name)} />
import { EmptyPage } from 'ebig-library'
<EmptyPage
title="No data found"
subtitle="Try adjusting your filters"
button={<Button label="Reset" onClick={reset} />}
/>
Typography component that renders semantic HTML elements with typography class helpers.
import { Text } from 'ebig-library'
<Text className="heading-3">Page title</Text>
<Text className="body-2" maxLine={2}>Truncated body text that wraps at two lines...</Text>
import { ComponentStatus, getStatusIcon } from 'ebig-library'
<ComponentStatus status="success" label="Completed" />
const icon = getStatusIcon('warning')
Pre-built form field wrappers integrating react-hook-form. All accept methods (from useForm()), name, label, required, disabled, placeholder, and className.
import { useForm } from 'react-hook-form'
import {
TextFieldForm,
TextAreaForm,
Select1Form,
SelectMultipleForm,
CheckboxForm,
SwitchForm,
DateTimePickerForm,
NumberPickerForm, // NumberPicker
RateForm, // Rating
ColorPickerForm,
GroupCheckboxForm,
GroupRadioButtonForm,
RangeForm, // Slider
CKEditorForm,
EbigEditorForm,
InputPasswordForm,
IconPickerForm,
UploadMultipleFileTypeForm,
} from 'ebig-library'
const methods = useForm()
<form onSubmit={methods.handleSubmit(onSubmit)}>
<TextFieldForm methods={methods} name="username" label="Username" required />
<TextFieldForm methods={methods} name="price" label="Price" type="money" />
<Select1Form
methods={methods}
name="category"
label="Category"
options={[{ id: '1', name: 'Tech' }, { id: '2', name: 'Finance' }]}
/>
<DateTimePickerForm methods={methods} name="dueDate" label="Due date" />
<EbigEditorForm methods={methods} name="description" label="Description" />
<Button label="Submit" type="submit" className="size40 button-primary" />
</form>
import { Util } from 'ebig-library'
// Date & Time
Util.dateTime_stringToDecimal('2026-04-08T10:00:00Z') // → Unix seconds
Util.stringToDate('08/04/2026', 'dd/mm/yyyy') // → Date
Util.calculateAge('01/01/2000') // → number
// Number & Currency
Util.formatCurrency(1500000, 'VND') // → "1,500,000.00 ₫"
Util.formatCurrency(99.9, 'USD') // → "$99.90"
Util.convertCurrency(100, 'USD', 'VND') // → 2450000
// Color
Util.hexToRgb('#287CF0') // → { r: 40, g: 124, b: 240 }
Util.rgbToHex(40, 124, 240) // → "#287cf0"
// String
Util.toSlug('Hello World!') // → "hello-world"
Util.randomGID() // → 32-char random hex ID
// Cookie
Util.getCookie('accessToken')
Util.setCookie('accessToken', token)
Util.clearCookie()
// File
Util.formatFileSize(1048576) // → "1 MB"
Util.stringToFile('content', 'file.txt')
// Auth
Util.encodeBase64('string')
Util.decodeBase64('encoded')
All controllers require ConfigData.url and ConfigData.pid to be set (done automatically by EbigProvider).
Generic CRUD for any project data module.
import { DataController } from 'ebig-library'
const ctrl = new DataController('Product')
// Fetch list (RediSearch query syntax)
const res = await ctrl.getListSimple({ page: 1, size: 20, query: '@Category:{Electronics}' })
// Get by ID
const item = await ctrl.getById('abc123')
// Add
await ctrl.add([{ Name: 'Product A', Price: 100 }])
// Edit
await ctrl.edit([{ Id: 'abc123', Price: 120 }])
// Delete
await ctrl.delete(['abc123', 'def456'])
// Aggregate / group
await ctrl.aggregateList({ filter: '@Price:[100 200]', sortby: [{ prop: 'Price', direction: 'ASC' }] })
For system-level setting entities (chart, form, card, view).
import { SettingDataController } from 'ebig-library'
const ctrl = new SettingDataController('form')
await ctrl.getListSimple({ page: 1, size: 10 })
await ctrl.action('add', { data: [{ Name: 'My Form' }] })
await ctrl.getByIds(['id1', 'id2'])
Login, get user info, and password utilities.
import { AccountController } from 'ebig-library'
const acc = new AccountController('Customer') // or 'User'
// Login with account/password
const res = await acc.login({ type: 'account', username: 'admin', password: 'pass' })
// Login with Google OAuth token
await acc.login({ type: 'google', token: googleToken })
// Get current user info
const info = await acc.getInfor()
// Hash a password
const hashed = await acc.hashPassword('mypassword')
Project-level queries (fetches eBig project metadata). Do not change the internal API paths.
import { EbigController } from 'ebig-library'
const ctrl = new EbigController('Project')
const project = await ctrl.getById('your-project-id')
const results = await ctrl.getListSimple({ query: '@Domain:{ebig.co}', size: 1 })
CRUD for schema-level settings (table, column, rel, menu, page, layout, designtoken, workflow, process, step).
import { TableController } from 'ebig-library'
const ctrl = new TableController('table')
const tables = await ctrl.getAll()
await ctrl.add([{ Name: 'Orders' }])
await ctrl.edit([{ Id: 'xxx', Name: 'OrdersV2' }])
await ctrl.delete(['xxx'])
import { IntegrationController } from 'ebig-library'
const integration = new IntegrationController()
await integration.sendEmail({
templateId: 'welcome-email',
templateParams: { to: 'user@example.com', name: 'Alice' }
})
Low-level HTTP helpers used by all controllers. Use directly only when you need calls outside the standard controller pattern.
import { BaseDA, ConfigData } from 'ebig-library'
// GET (auto-injects auth headers for ConfigData.url requests)
const data = await BaseDA.get(`${ConfigData.url}data/custom-endpoint`, {
headers: { pid: ConfigData.pid }
})
// POST
await BaseDA.post(`${ConfigData.url}data/action?action=add`, {
headers: { pid: ConfigData.pid, module: 'Product' },
body: { data: [{ Name: 'Item' }] }
})
// Upload files (auto-batches: max 12 files / 200 MB per batch)
const uploaded = await BaseDA.uploadFiles(fileList)
// uploaded → [{ Id, Url, Name, ... }]
Render entire UI sections driven by eBig backend configuration. Requires a valid pid/url in EbigProvider.
import { PageById, PageByUrl, FormById, CardById, ViewById, ChartById, ChartByType } from 'ebig-library'
// Render a full page by its backend ID
<PageById id="page-id" />
// Render a page by URL path (matches current URL)
<PageByUrl url="location.pathname" />
// Render a form by ID
<FormById id="form-id" onSuccess={(data) => console.log(data)} />
// Render a card layout by ID
<CardById id="card-id" />
// Render a data view by ID
<ViewById id="view-id" />
// Chart by backend config ID
<ChartById id="chart-id" />
// Chart by type with your own data
<ChartByType type="bar" data={chartData} />
Design tokens are loaded from the eBig backend at runtime and injected as CSS custom properties into <head>. Toggle dark mode via setTheme:
const { theme, setTheme } = useEbigContext()
setTheme('dark') // adds class="dark" to <html>
Tokens follow the pattern:
/* Light mode */
--primary-main-color: #287CF0;
--neutral-text-title-color: #18181B;
/* Dark mode (html.dark) */
--primary-main-color: #4A90E2;
--neutral-text-title-color: #EAEAEC;
The layout system uses a 24-column grid. Add these classes to children inside a row.
| Class | Columns | Notes |
|---|---|---|
col1 – col24 | 1/24 – 24/24 | Always applied |
remain | fills rest | flex: 1 |
col1-min – col24-min | at < 576px | phones |
col1-sm – col24-sm | at ≥ 576px | small devices |
col1-md – col24-md | at ≥ 768px | tablets |
col1-lg – col24-lg | at ≥ 992px | laptops |
col1-xl – col24-xl | at ≥ 1200px | desktops |
col1-xxl – col24-xxl | at > 1200px | wide screens |
// 2-column on desktop, stacks on mobile
<div className="row" style={{ gap: 16 }}>
<div className="col24 col12-lg" style={{ "--gutter": "16px" }}>Left panel</div>
<div className="col24 col12-lg" style={{ "--gutter": "16px" }}>Right panel</div>
</div>
MIT © eBig / FDITECH
FAQs
A modern UI component library for React by eBig
The npm package ebig-library receives a total of 1,051 weekly downloads. As such, ebig-library popularity was classified as popular.
We found that ebig-library demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

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.

Security News
Feross Aboukhadijeh joins TBPN to discuss Socket's $60M Series C, 500%+ ARR growth, AI's impact on open source, and the rise in supply chain attacks.

Security News
OSV withdrew 157 OSV malware reports after automated false positives incorrectly flagged trusted npm and PyPI packages, sending bad records into tools that rely on OSV data.

Research
/Security News
TrapDoor crypto stealer hits 36 malicious packages across npm, PyPI, and Crates.io, targeting crypto, DeFi, AI, and security developers.