@unilic/react
v1.0.0
Published
React hooks and components for Universal License SDK
Maintainers
Readme
@unilic/react
React hooks and components for Universal License SDK.
Why This Package?
While @unilic/client works in any JavaScript environment, this package provides React-specific
conveniences:
✅ Hooks - React-friendly state management with automatic caching
✅ Context - Share SDK client across your app via React Context
✅ Components - Pre-built guards and gates for common patterns
✅ TypeScript - Full type safety with your existing types
✅ Tree-Shakeable - Import only what you need
Installation
npm install @unilic/react @unilic/client
# or
yarn add @unilic/react @unilic/client
# or
pnpm add @unilic/react @unilic/clientQuick Start
1. Wrap Your App
import { LicenseProvider } from '@unilic/react';
function App() {
return (
<LicenseProvider
config={{
baseUrl: process.env.REACT_APP_LICENSE_API_URL!,
cache: true,
}}
>
<YourApp />
</LicenseProvider>
);
}2. Use Hooks
import { useLicenseValidation } from '@unilic/react';
function OnboardingPage() {
const [key, setKey] = useState('');
const { validation, loading, validate } = useLicenseValidation();
const handleSubmit = () => validate(key);
return (
<div>
<input value={key} onChange={(e) => setKey(e.target.value)} />
<button onClick={handleSubmit} disabled={loading}>
Activate License
</button>
{validation?.valid && <p>License is valid!</p>}
</div>
);
}3. Guard Features
import { FeatureGate } from '@unilic/react';
function Dashboard() {
return (
<FeatureGate feature="advancedReporting" fallback={<UpgradePrompt />}>
<AdvancedReports />
</FeatureGate>
);
}API Reference
Hooks
useLicense(licenseKey)
Fetch and manage a single license.
const { license, loading, error, refetch } = useLicense('LICENSE-KEY');useLicenseValidation()
Validate licenses with automatic device fingerprinting.
const { validation, loading, error, validate } = useLicenseValidation();
await validate('LICENSE-KEY', { requiredTier: 'pro' });useProducts()
Fetch products and plans.
const { products, loading, getPlans } = useProducts();
const plans = await getPlans('PRODUCT-CODE');usePurchase()
Handle purchase workflows.
const { createOrder, completePurchase, loading } = usePurchase();
const order = await createOrder({ planCode, organizationData });useFeatureFlag(feature, licenseKey?)
Check if a feature is available.
const hasFeature = useFeatureFlag('advancedReporting');Components
<LicenseProvider>
Context provider for the SDK client.
Props:
config: SDKConfig- SDK configuration
<LicenseGuard>
Protect routes based on license status.
Props:
licenseKey: string- License key to checkrequiredStatus?: string[]- Required statuses (default:['active'])fallback?: ReactNode- Content to show if invalidloadingFallback?: ReactNode- Loading statechildren: ReactNode- Protected content
<LicenseGuard licenseKey={licenseKey} fallback={<RedirectToRenewal />}>
<ProtectedContent />
</LicenseGuard><FeatureGate>
Show/hide UI based on features.
Props:
feature: string- Feature to checklicenseKey?: string- Optional license key (defaults to localStorage)fallback?: ReactNode- Content to show if feature unavailablechildren: ReactNode- Feature content
<FeatureGate feature="advancedReporting" fallback={<UpgradeButton />}>
<AdvancedReportsUI />
</FeatureGate>Patterns
Onboarding Flow
import { useLicenseValidation, DeviceFingerprint } from '@unilic/react';
function Onboarding() {
const [key, setKey] = useState('');
const { validate, validation, loading } = useLicenseValidation();
const router = useRouter();
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
const result = await validate(key, {
requiredTier: 'standard',
requiredFeatures: ['memberManagement'],
});
if (result.valid) {
router.push('/dashboard');
}
};
return (
<form onSubmit={handleSubmit}>
<input value={key} onChange={(e) => setKey(e.target.value)} placeholder="Enter license key" />
<button type="submit" disabled={loading}>
{loading ? 'Validating...' : 'Continue'}
</button>
{validation && !validation.valid && <p className="error">{validation.error}</p>}
</form>
);
}Feature-Based UI
import { FeatureGate } from '@unilic/react';
function App() {
return (
<div>
{/* Always visible */}
<BasicDashboard />
{/* Only for users with 'advancedReporting' feature */}
<FeatureGate feature="advancedReporting">
<AdvancedReports />
</FeatureGate>
{/* Only for users with 'apiAccess' feature */}
<FeatureGate
feature="apiAccess"
fallback={
<div>
<p>API access is available in Pro tier</p>
<button>Upgrade to Pro</button>
</div>
}
>
<APIKeysManager />
</FeatureGate>
</div>
);
}Protected Routes
import { LicenseGuard } from '@unilic/react';
import { useRouter } from 'next/router';
function ProtectedRoute({ children }) {
const licenseKey = localStorage.getItem('licenseKey');
const router = useRouter();
return (
<LicenseGuard
licenseKey={licenseKey || ''}
fallback={
<div>
<h2>License Required</h2>
<p>Please activate your license to continue.</p>
<button onClick={() => router.push('/activate')}>Activate License</button>
</div>
}
>
{children}
</LicenseGuard>
);
}Integration Notes
This package is designed to be dropped into typical React apps:
- Use hooks for data fetching and state management.
- Use
LicenseProviderto share a single client instance. - Use
LicenseGuard/FeatureGateto conditionally render UI.
Examples
See examples/ for complete integration examples:
- basic-setup.tsx - Minimal onboarding setup
- dashboard-integration.tsx - Full dashboard with feature gates
TypeScript Support
All hooks and components are fully typed:
import type { License, ValidateLicenseResponse } from '@unilic/react';
const { license }: { license: License | null } = useLicense(key);
const { validation }: { validation: ValidateLicenseResponse | null } = useLicenseValidation();Testing
# Run tests
npm test
# Watch mode
npm test:watch
# Coverage
npm test -- --coverageLicense
This project is licensed under the MIT License - see the LICENSE file for details.
