React Integration
FrontAlign is React-ready from the same frontalign package. You do not install a separate React package.
Use the CSS from frontalign/css and React hooks from frontalign/react.
Install
npm install frontalign
Import
import 'frontalign/css';
import {
useAccordion,
useAlert,
useAlertDismiss,
useBadge,
useCarousel,
useCollapse,
useDarkMode,
useDrawer,
useDropdown,
useForm,
useModal,
useNavbar,
usePopover,
useRange,
useSelect,
useSkeleton,
useSwiper,
useTabview,
useToast,
useTooltip
} from 'frontalign/react';
Recommended App Setup
FrontAlign React hooks are client-side hooks. In Next.js, place them inside a client component.
'use client';
import {
useAccordion,
useAlertDismiss,
useCollapse,
useDrawer,
useDropdown,
useForm,
useNavbar,
} from "frontalign/react";
export function FrontAlignProvider({ children }) {
useNavbar();
useDrawer();
useDropdown();
useCollapse();
useAccordion();
useAlertDismiss();
useForm();
return children;
}
Then wrap your app:
// app/layout.jsx
import { FrontAlignProvider } from './providers';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<FrontAlignProvider>
{children}
</FrontAlignProvider>
</body>
</html>
);
}
Delegated Hooks
These hooks initialize FrontAlign DOM behavior globally and automatically clean up on unmount.
'use client';
import {
useNavbar,
useDrawer,
useDropdown,
useCollapse,
useAccordion,
useAlertDismiss,
useForm,
} from "frontalign/react";
export function UIEngine() {
useNavbar();
useDrawer();
useDropdown();
useCollapse();
useAccordion();
useAlertDismiss();
useForm();
return null;
}
Delegated Hook Reference
| Hook | Purpose | Return |
|---|---|---|
useNavbar() | Responsive navbar toggle behavior | void |
useDrawer() | Drawer open, close, backdrop, Escape key and focus trap | void |
useDropdown() | Dropdown click, hover and Escape behavior | void |
useCollapse() | Collapse and accordion-group behavior | void |
useAccordion() | Accordion open and close behavior | void |
useAlertDismiss() | Dismissible static alerts | void |
useForm() | Validation, file input helpers, floating labels and range helpers | void |
Navbar Example
'use client';
import { useNavbar } from 'frontalign/react';
export function NavbarExample() {
useNavbar();
return (
<nav className="navbar">
<a className="navbar-brand" href="/">FrontAlign</a>
<button
className="navbar-toggler"
fa-toggle="navbar"
data-target="#main-menu"
aria-label="Toggle navigation"
>
</button>
<div id="main-menu" className="navbar-menu">
</div>
</nav>
);
}
Drawer Example
'use client';
import { useDrawer } from "frontalign/react";
export function DrawerExample() {
useDrawer();
return (
<>
<button className="button is-primary" fa-toggle="drawer" data-target="#demo-drawer">
Open Drawer
</button>
<div id="demo-drawer" className="drawer is-end">
<div className="drawer-panel">
</div>
</div>
</>
);
}
Dropdown
useDropdown() initializes dropdown click and hover interactions with accessibility support (aria-expanded, Escape key).
'use client';
import { useDropdown } from 'frontalign/react';
export function DropdownExample() {
useDropdown();
return (
<div className="dropdown">
<button className="button" fa-toggle="dropdown">
Options
</button>
<div className="dropdown-menu">
<a className="dropdown-item" href="#">Edit</a>
<a className="dropdown-item" href="#">Duplicate</a>
<hr className="dropdown-divider" />
<a className="dropdown-item is-danger" href="#">Delete</a>
</div>
</div>
);
}
Collapse
useCollapse() initializes collapsible elements with slide/fade animations and accordion group support.
'use client';
import { useCollapse } from "frontalign/react";
export function CollapseExample() {
useCollapse();
return (
<>
<button className="button" fa-toggle="collapse" data-target="#my-collapse">
Toggle content
</button>
<div id="my-collapse" className="collapse">
<div className="collapse-body">
<p>This content is toggled by <code>useCollapse()</code>.</p>
</div>
</div>
</>
);
}
Accordion
useAccordion() initializes accessible accordion interactions with animated open/close behavior.
'use client';
import { useAccordion } from 'frontalign/react';
export function AccordionExample() {
useAccordion();
return (
<div className="accordion"fa-component="accoridon">
<div className="accordion-item">
<button className="accordion-header">What is FrontAlign?</button>
<div className="accordion-body">
<p>FrontAlign is a UI component library with React integration out of the box.</p>
</div>
</div>
<div className="accordion-item">
<button className="accordion-header">How do I install it?</button>
<div className="accordion-body">
<p>Run <code>npm install frontalign</code> and import the CSS and hooks.</p>
</div>
</div>
</div>
);
}
Alert Dismiss
useAlertDismiss() initializes dismissible static alerts with persistence via localStorage and animation support.
'use client';
import { useAlertDismiss } from "frontalign/react";
export function AlertDismissExample() {
useAlertDismiss();
return (
<div className="alert is-info" id="welcome-alert"fa-component="alert">
<p>Welcome! This alert can be dismissed.</p>
<button className="alert-close" aria-label="Close alert"></button>
</div>
);
}
Form
useForm() initializes form validation, file upload helpers, floating labels and range slider interactions.
'use client';
import { useForm } from 'frontalign/react';
export function FormExample() {
useForm();
return (
<form className="form" fa-component="form"noValidate>
<div className="form-group is-floating">
<input id="email" type="email" className="form-input"data-rule="email" />
<label htmlFor="email">Email address</label>
</div>
<div className="form-group">
<input type="range" className="form-range"fa-component="range" min="0" max="100" defaultValue="40" />
</div>
<button type="submit" className="button is-primary">Submit</button>
</form>
);
}
Carousel
useCarousel(target, options) accepts either a React ref or a CSS selector string for target. Pass null to use the hook's own ref. Returns { ref, next, prev, go } for imperative control. The instance is automatically disposed on unmount.
'use client';
import { useCarousel } from 'frontalign/react';
export function CarouselExample() {
const { ref, next, prev, go } = useCarousel(null, {
mode: 'slide',
loop: true,
controls: true,
pager: true,
swipe: true,
autoplay: {
enabled: true,
interval: 4000,
pauseOnHover: true,
pauseOnSwipe: true
}
});
return (
<section>
<div className="carousel" ref={ref}>
<div className="carousel-items">
<div className="item">Slide 1</div>
<div className="item">Slide 2</div>
<div className="item">Slide 3</div>
</div>
</div>
</section>
);
}
Carousel with a Selector
If you don't need the ref (for example, the markup already exists outside React's control), pass a CSS selector as target instead of null.
'use client';
import { useCarousel } from 'frontalign/react';
export function CarouselSelectorExample() {
const { next, prev } = useCarousel('#hero-carousel', { loop: true });
return (
<section>
<div id="hero-carousel" className="carousel">
<div className="carousel-items">
<div className="item">Slide 1</div>
<div className="item">Slide 2</div>
</div>
</div>
</section>
);
}
Tooltip
useTooltip(options) returns a ref. Attach it to the trigger element. The instance is automatically disposed on unmount.
'use client';
import { useTooltip } from 'frontalign/react';
export function TooltipExample() {
const tipRef = useTooltip({
message: 'Copy to clipboard',
placement: 'top',
trigger: 'hover',
hasArrow: true
});
return (
<button ref={tipRef} className="button is-primary">
Copy
</button>
);
}
Popover
usePopover(options) returns a ref. Attach it to the trigger element. The instance is automatically disposed on unmount.
'use client';
import { usePopover } from 'frontalign/react';
export function PopoverExample() {
const popoverRef = usePopover({
title: 'Info',
content: 'Popover content',
placement: 'bottom',
trigger: 'click',
closeOnOutsideClick: true,
closeOnEscape: true
});
return (
<button ref={popoverRef} className="button is-primary">
Open
</button>
);
}
Popover with Target Content
Use target to source the popover content from an existing element (for example a <template>) instead of passing content directly.
'use client';
import { usePopover } from 'frontalign/react';
export function PopoverTargetExample() {
const popoverRef = usePopover({
target: '#popover-template',
placement: 'right',
trigger: 'click'
});
return (
<>
<button ref={popoverRef} className="button">
Show details
</button>
<template id="popover-template">
<strong>Shipping</strong>
<p>Orders ship within 2 business days.</p>
</template>
</>
);
}
Tabview
useTabview() returns a ref. Attach it to the tab container. Handles tab switching, the animated underline, and ResizeObserver cleanup automatically.
'use client';
import { useTabview } from "frontalign/react";
export function TabviewExample() {
const tabRef = useTabview();
return (
<>
<div fa-component="tabview" ref={tabRef} className="tabview">
<button className="tab-item is-active" data-tab="#panel-one">One</button>
<button className="tab-item" data-tab="#panel-two">Two</button>
</div>
<div id="panel-one" className="tab-panel is-open">First panel</div>
<div id="panel-two" className="tab-panel">Second panel</div>
</>
);
}
Swiper
useSwiper() returns a ref for pointer-based horizontal drag behavior. Pointer listeners are removed automatically on unmount.
'use client';
import { useSwiper } from 'frontalign/react';
export function SwiperExample() {
const swiperRef = useSwiper();
return (
<div fa-component="swiper" className="swiper" ref={swiperRef}>
<div className="swiper-wrapper">
<article className="card">Card 1</article>
<article className="card">Card 2</article>
<article className="card">Card 3</article>
</div>
</div>
);
}
Badge
useBadge(count) returns a ref and formats large numbers as 99+. The effect re-runs whenever count changes.
'use client';
import { useBadge } from "frontalign/react";
export function BadgeExample({ count = 128 }) {
const badgeRef = useBadge(count);
return (
<span className="badge is-danger" data-count={count} ref={badgeRef}></span>
);
}
Toast
useToast() does not return a ref — it returns status methods that create and show a toast directly.
'use client';
import { useToast } from 'frontalign/react';
export function ToastExample() {
const toast = useToast();
return (
<button
className="button is-success"
onClick={() => toast.success('Profile updated successfully.')}
>
Save
</button>
);
}
| Method | Usage |
|---|---|
toast.success(message, options) | Success toast |
toast.danger(message, options) | Danger toast |
toast.warning(message, options) | Warning toast |
toast.info(message, options) | Info toast |
toast.show(options) | Custom toast — pass message, status, position, etc. directly |
Modal
useModal() does not return a ref — it returns promise-based helpers that mirror Modal.alert(), Modal.confirm(), Modal.custom(), and the Modal.queue API.
'use client';
import { useModal } from "frontalign/react";
export function ModalExample() {
const { alert, confirm } = useModal();
async function handleDelete() {
const confirmed = await confirm({
heading: 'Delete item?',
content: 'This action cannot be undone.'
});
if (confirmed) {
await alert({
heading: 'Deleted',
content: 'The item was deleted successfully.'
});
}
}
return (
<button className="button is-danger" onClick={handleDelete}>
Delete
</button>
);
}
Custom Modal
'use client';
import { useModal } from "frontalign/react";
export function CustomModalExample() {
const { custom } = useModal();
return (
<button className="button" onClick={() => custom({ id: '#my-modal' })}>
Open custom modal
</button>
);
}
Queued Modals
queue.alert() and queue.confirm() mirror alert() and confirm(), but wait their turn if another modal is already open or queued.
'use client';
import { useModal } from "frontalign/react";
export function QueuedModalExample() {
const { queue } = useModal();
async function runSteps() {
await queue.alert({ heading: 'Step 1', content: 'First modal' });
const proceed = await queue.confirm({ heading: 'Step 2', content: 'Continue?' });
if (proceed) {
await queue.alert({ heading: 'Step 3', content: 'All done.' });
}
}
return (
<button className="button" onClick={runSteps}>
Start steps
</button>
);
}
Dynamic Alert
useAlert() does not return a ref — it returns status methods that insert an alert relative to a target selector. Use position to control whether it's inserted 'before' or 'after' the target.
'use client';
import { useAlert } from "frontalign/react";
export function AlertExample() {
const alert = useAlert();
return (
<>
<div id="form-message"></div>
<button
className="button is-primary"
onClick={() => alert.success('#form-message', {
message: 'Profile saved.',
position: 'after'
})}
>
Show Alert
</button>
</>
);
}
Select
useSelect(target, options) accepts either a CSS selector string or a React ref pointing to a native <select> element for target. It does not return a ref — it returns { instance }, a mutable ref to the underlying Select instance. Use the onChange option to sync the selected value into React state.
'use client';
import { useState } from 'react';
import { useSelect } from 'frontalign/react';
export function SelectExample() {
const [country, setCountry] = useState('');
useSelect('#country', {
data: [
{ value: 'az', name: 'Azerbaijan' },
{ value: 'tr', name: 'Turkey' },
],
placeholder: 'Select country',
onChange: setCountry,
});
return (
<>
<select id="country"></select>
<p>Selected: {country || 'None'}</p>
</>
);
}
Select with a Ref
target also accepts a React ref instead of a selector — useful when the <select> is rendered conditionally or you want to avoid relying on a global id.
'use client';
import { useRef, useState } from 'react';
import { useSelect } from 'frontalign/react';
export function MultiSelectExample() {
const selectRef = useRef(null);
const [tags, setTags] = useState([]);
useSelect(selectRef, {
data: [
{ value: 'react', name: 'React' },
{ value: 'vue', name: 'Vue' },
],
multiple: true,
search: true,
onChange: (value) => setTags(value.split(',')),
});
return <select ref={selectRef} />;
}
Skeleton
useSkeleton() returns { ref, show, hide, error, wrap }. Attach ref to the element you want to mask. If the element has a data-skeleton attribute, it activates automatically on mount.
'use client';
import { useEffect } from "react";
import { useSkeleton } from "frontalign/react";
export function SkeletonExample() {
const { ref, wrap } = useSkeleton();
useEffect(() => {
wrap(async () => {
await new Promise((resolve) => setTimeout(resolve, 800));
}, {
message: 'Could not load content',
retry: true
});
}, []);
return (
<div ref={ref} data-skeleton-layout="card">
<h3>Loaded content</h3>
<p>This content is protected by the skeleton state.</p>
</div>
);
}
Manual show / hide / error
'use client';
import { useSkeleton } from "frontalign/react";
export function SkeletonManualExample() {
const { ref, show, hide, error } = useSkeleton();
return (
<>
<div ref={ref} data-skeleton-layout="list">
<p>Content goes here.</p>
</div>
<button className="button" onClick={show}>Show</button>
<button className="button" onClick={hide}>Hide</button>
<button
className="button is-danger"
onClick={() => error({ message: 'Failed to load', retry: true })}
>
Trigger error
</button>
</>
);
}
Skeleton Methods
| Method | Description |
|---|---|
show() | Activates the skeleton state on the ref element |
hide() | Removes the skeleton state |
error(options) | Shows an error state, with optional message and retry |
wrap(asyncFn, options) | Shows the skeleton, runs asyncFn, then hides on success or shows the error state on failure |
Dark Mode
useDarkMode(options) does not return a ref — it returns { isDark }. Pass onChange in the options object to sync the theme into React state; the hook calls it internally whenever the theme changes.
'use client';
import { useState } from 'react';
import { useDarkMode } from 'frontalign/react';
export function DarkModeExample() {
const [dark, setDark] = useState(false);
const { isDark } = useDarkMode({
autoCreateBtn: false,
customBtn: '#theme-toggle',
onChange: setDark
});
return (
<button id="theme-toggle" className="button is-light-outline">
{dark || isDark() ? 'Dark' : 'Light'}
</button>
);
}
Auto-Created Toggle Button
Omit customBtn and leave autoCreateBtn at its default to let FrontAlign mount a floating toggle button for you.
'use client';
import { useDarkMode } from 'frontalign/react';
export function DarkModeAutoExample() {
useDarkMode();
return null;
}
Range
useRange(options) mounts a FrontAlign Range instance on the wrapper element. Attach ref to the element carrying fa-component="range" — never to the <input type="range"> itself, since Range locates its input(s) by querying inside that wrapper. Returns { ref, value, getValue, setValue, refresh, instance }. The instance is automatically disposed on unmount.
Options are read once, at mount.
tooltip,ticks,valueLabel,onInput, andonChangeare captured the first time the component renders and are not reactive — passing a new value for any of them on a later render has no effect on the mounted instance. If a callback needs access to fresh state, use a ref to hold the latest value internally, or remount the component (e.g. with a changingkey) instead of expecting the hook to pick up new options.
The hook calls Range.getOrCreateInstance() rather than new Range(), so it's safe under React's Strict Mode double-invocation in development — mounting twice reuses the existing instance instead of creating a duplicate.
'use client';
import { useRange } from 'frontalign/react';
export function VolumeSliderExample() {
const range = useRange({
valueLabel: (v) => `${v}%`,
onChange: (value) => console.log('Committed value:', value),
});
return (
<div fa-component="range" ref={range.ref}>
<input type="range" min="0" max="100" defaultValue="50" />
</div>
);
}
Dual (Min/Max) Range
Pass two input[type="range"] elements inside the wrapper and Range automatically switches to dual mode: a connecting fill track renders between the thumbs, and value becomes a [min, max] array instead of a single number.
'use client';
import { useRange } from 'frontalign/react';
export function PriceRangeExample() {
const range = useRange({
valueLabel: (v) => `$${v}`,
onChange: ([min, max]) => {
fetchProducts({ minPrice: min, maxPrice: max });
},
});
return (
<div fa-component="range" ref={range.ref}>
<input type="range" min="0" max="1000" defaultValue="200" />
<input type="range" min="0" max="1000" defaultValue="800" />
</div>
);
}
Live Value in React State
value updates automatically on every input (drag) and change (release) event — no manual event wiring needed to reflect it in your UI.
'use client';
import { useRange } from 'frontalign/react';
export function RatingSliderExample() {
const range = useRange();
return (
<div>
<div fa-component="range" ref={range.ref}>
<input type="range" min="1" max="10" defaultValue="5" />
</div>
<p>Selected rating: {range.value}</p>
</div>
);
}
Ticks from a Datalist
ticks (enabled by default) reads tick marks from a <datalist> referenced via the input's list attribute. Ticks only apply to single-input ranges, not dual ranges.
'use client';
import { useRange } from 'frontalign/react';
export function SizeSliderExample() {
const range = useRange();
return (
<div fa-component="range" ref={range.ref}>
<input type="range" min="0" max="3" step="1" list="sizes" defaultValue="1" />
<datalist id="sizes">
<option value="0" label="XS" />
<option value="1" label="S" />
<option value="2" label="M" />
<option value="3" label="L" />
</datalist>
</div>
);
}
Live Updates vs. Committed Changes (onInput vs onChange)
onInput fires on every drag/keyboard movement, onChange fires once when the value is committed (pointer release, blur, or keyup). Both are called with (value, index) — index identifies which thumb moved (0 or 1) and is only meaningful for dual ranges; for single ranges it's always 0.
'use client';
import { useState } from 'react';
import { useRange } from 'frontalign/react';
export function LiveVsCommittedExample() {
const [dragging, setDragging] = useState(null);
const range = useRange({
onInput: (value, index) => setDragging({ value, index }),
onChange: (value, index) => {
setDragging(null);
console.log(`Thumb ${index} committed at`, value);
},
});
return (
<div>
<div fa-component="range" ref={range.ref}>
<input type="range" min="0" max="1000" defaultValue="200" />
<input type="range" min="0" max="1000" defaultValue="800" />
</div>
{dragging && <p>Dragging thumb {dragging.index}: {dragging.value}</p>}
</div>
);
}
Reading the Value On Demand (getValue)
value is kept in sync with React state, but state updates are asynchronous — inside a synchronous callback like a submit handler, getValue() reads the current value straight from the Range instance instead of relying on a possibly-stale value closure.
'use client';
import { useRange } from 'frontalign/react';
export function SubmitOnDemandExample() {
const range = useRange();
const handleSubmit = () => {
const current = range.getValue();
saveFilter({ threshold: current });
};
return (
<div>
<div fa-component="range" ref={range.ref}>
<input type="range" min="0" max="100" defaultValue="50" />
</div>
<button className="button is-primary" onClick={handleSubmit}>
Apply
</button>
</div>
);
}
Programmatic Control
Use setValue to update the slider imperatively (accepts a number for single ranges or [min, max] for dual ranges), and refresh after changing min/max/step/list attributes on the DOM directly — Range has no MutationObserver watching those attributes, so visuals go stale silently without it.
'use client';
import { useRange } from 'frontalign/react';
export function ResetButtonExample() {
const range = useRange();
return (
<div>
<div fa-component="range" ref={range.ref}>
<input type="range" min="0" max="100" defaultValue="50" />
</div>
<button className="button" onClick={() => range.setValue(50)}>
Reset
</button>
</div>
);
}
Range Options
| Option | Type | Default | Description |
|---|---|---|---|
tooltip | boolean | true | Shows a floating tooltip with the current value while dragging or focused |
ticks | boolean | true | Renders tick marks from the input's linked <datalist>. Ignored for dual ranges |
valueLabel | string | function | null | null | Formats the displayed value. A string uses as a placeholder; a function receives the raw value and returns the formatted label |
onInput | function | — | Called on every drag/keyboard movement with the live value (and thumb index for dual ranges) |
onChange | function | — | Called once the value is committed (pointer release, blur, or keyup) |
Range Return Values
| Property | Type | Description |
|---|---|---|
ref | RefObject | Attach to the fa-component="range" wrapper element, not the <input> |
value | number | number[] | Live value, kept in sync with React state. A single number, or [min, max] for dual ranges |
getValue() | function | Reads the current value directly from the instance |
setValue(value) | function | Sets the value imperatively and re-renders the fill, tooltip, and aria-valuetext |
refresh() | function | Re-reads min/max/step/list from the DOM and re-renders ticks, fill, and tooltip position |
instance | Range | null | The underlying Range class instance, for advanced/imperative use |
Complete Export Reference
| Export | Returns | Type |
|---|---|---|
useAccordion | void | Delegated Hook |
useAlert | show, success, danger, warning, info | Hook |
useAlertDismiss | void | Delegated Hook |
useBadge | ref | Hook |
useCarousel | ref, next, prev, go | Hook |
useCollapse | void | Delegated Hook |
useDarkMode | isDark | Hook |
useDrawer | void | Delegated Hook |
useDropdown | void | Delegated Hook |
useForm | void | Delegated Hook |
useModal | alert, confirm, custom, queue | Hook |
useNavbar | void | Delegated Hook |
usePopover | ref | Hook |
useRange | ref, value, getValue, setValue, refresh, instance | Hook |
useSelect | instance | Hook |
useSkeleton | ref, show, hide, error, wrap | Hook |
useSwiper | ref | Hook |
useTabview | ref | Hook |
useToast | success, danger, warning, info, show | Hook |
useTooltip | ref | Hook |
Important Notes
- There is no separate package like
frontalign-react. - Install only
frontalign. - Import React hooks from
frontalign/react. - Import CSS once from
frontalign/css. - Hooks that return a ref (
useTooltip,usePopover,useTabview,useSwiper,useBadge, thereffromuseSkeleton,useCarousel, anduseRange) must have that ref attached to a DOM element, or the underlying instance is never created. useRange's ref belongs on the wrapper, not the<input type="range">—Rangefinds its input(s) by querying inside the element the ref is attached to.useRange's options are captured once, at mount.tooltip,ticks,valueLabel,onInput, andonChangewon't update on re-render if you pass new values — remount (e.g. viakey) if the underlying config genuinely needs to change.useRangeis Strict Mode safe. It mounts throughRange.getOrCreateInstance(), so React's development-mode double-invocation reuses the existing instance instead of creating a second one.- Hooks that don't return a ref (
useAlert,useToast,useModal,useDarkMode) work against a selector or globally — norefattribute needed in JSX. useCarouselanduseSelectaccept either a ref or a CSS selector string as their first argument — pick whichever fits how the markup is rendered.useSelectreturns{ instance }, not a ref — bind the<select>element with a plainid(selector mode) or pass your own ref as thetargetargument (ref mode).- Do not manually create
new Carousel(),new Toast(),new Modal(),new Popover(), ornew Tooltip()in React pages when a hook exists. - Use client components in Next.js (
'use client') because every hook depends on browser APIs after mount.