Package Exports
- @citiwave/im-disclaimer-modal
Readme
@citiwave/im-disclaimer-modal
A customizable React disclaimer modal component with theme support. This component is designed to enhance user experience by providing clear and responsive terms agreements.
Installation
To install the package, use one of the following commands:
npm install @citiwave/im-disclaimer-modal
# or
yarn add @citiwave/im-disclaimer-modalQuick Start
The package works with both JavaScript (JSX) and TypeScript (TSX) files. Import the styles based on your bundler:
// For Vite
import '@citiwave/im-disclaimer-modal/styles.css';
// For webpack or other bundlers
import '@citiwave/im-disclaimer-modal/style.css';JavaScript (JSX)
import { DisclaimerProvider } from '@citiwave/im-disclaimer-modal';
function App() {
const disclaimerSections = [
{
title: 'Terms of Service',
required: true,
content: <div>Your terms content here...</div>
}
];
return (
<DisclaimerProvider
id="my-disclaimer" // Unique ID for localStorage
title="Agreement Required" // Modal title
disclaimerSections={disclaimerSections}
theme="light" // or "dark"
/>
);
}
export default App;TypeScript (TSX)
import { DisclaimerProvider, type DisclaimerSection } from '@citiwave/im-disclaimer-modal';
const App: React.FC = () => {
const disclaimerSections: DisclaimerSection[] = [
{
title: 'Terms of Service',
required: true,
content: <div>Your terms content here...</div>
}
];
return (
<DisclaimerProvider
id="my-disclaimer"
title="Agreement Required"
disclaimerSections={disclaimerSections}
theme="light"
/>
);
};
export default App;The modal will automatically:
- Show on first visit
- Save the user's choice in localStorage
- Hide after acceptance
- Not show again on future visits
Advanced Usage
For more control over the modal's behavior, you can use the lower-level components:
Using useDisclaimer Hook
import { DisclaimerModal, useDisclaimer } from '@citiwave/im-disclaimer-modal';
import '@citiwave/im-disclaimer-modal/style.css';
const MyComponent = () => {
const { isOpen, onClose, reset } = useDisclaimer('unique-id', {
storagePrefix: 'my-app',
defaultOpen: true
});
return (
<DisclaimerModal
isOpen={isOpen}
onClose={onClose}
title="Agreement Required"
disclaimerSections={[...]}
theme="light"
/>
);
};Requirements
To render the DisclaimerModal correctly, the following dependencies are required:
lucide-reactversion^0.475.0or higher.
If the required version of lucide-react is not available, the modal will automatically use fallback icons to ensure functionality.
Fallback Icons
In case the lucide-react library cannot be resolved, the modal will display default icons to maintain usability. This ensures that the modal remains functional even if the required icons are not available.
Features
- Dark and Light theme support
- Responsive design for all devices
- TypeScript support
- Customizable themes to match your application
- Local storage persistence for user preferences
- Smooth animations for a better user experience
- Multi-section support for comprehensive agreements
- JSON-based content structure
- Content rendering utilities for dynamic content
Usage
The DisclaimerModal will automatically display on page load for first-time visitors. Once a user accepts the disclaimer, their choice is saved in localStorage and the modal won't show again unless explicitly reset.
Basic Usage with JSON Content
import { DisclaimerModal, useDisclaimer } from '@citiwave/im-disclaimer-modal';
import '@citiwave/im-disclaimer-modal/style.css'; // Required: Import the styles
const MyComponent = () => {
// The modal will automatically show on first visit
const { isOpen, onClose } = useDisclaimer('unique-id', {
storagePrefix: 'my-app', // Optional: prefix for localStorage key
defaultOpen: true // Optional: controls initial state for new visitors (default: true)
});
// Your JSON content structure
const disclaimerContent = {
"terms": {
"title": "Terms",
"required": true,
"content": {
"title": "Important Information",
"sections": [
{
"heading": "Forward-Looking Statements",
"text": "Your disclaimer text here..."
},
{
"heading": "Additional Terms",
"text": "More terms...",
"list": [
"Item 1",
"Item 2",
"Item 3"
]
}
]
}
}
};
// Transform the disclaimer sections
const disclaimerSections = Object.entries(disclaimerContent).map(([key, section]) => ({
title: section.title,
required: section.required,
content: renderDisclaimerContent(section.content, 'light') // or 'dark' for dark theme
}));
return (
<DisclaimerModal
isOpen={isOpen}
onClose={onClose}
title="Agreement Required"
disclaimerSections={disclaimerSections}
theme="light" // or "dark"
/>
);
};Simple Usage with Static Content
For simpler use cases, you can also use static content:
import { DisclaimerModal, useDisclaimer } from '@citiwave/im-disclaimer-modal';
const MyComponent = () => {
const { isOpen, onClose } = useDisclaimer('unique-id', {
storagePrefix: 'my-app',
defaultOpen: true
});
const disclaimerSections = [
{
title: 'Terms of Service',
required: true,
content: <div>Your terms content here...</div>
},
{
title: 'Privacy Policy',
required: false,
content: <div>Your privacy policy content here...</div>
}
];
return (
<DisclaimerModal
isOpen={isOpen}
onClose={onClose}
title="Agreement Required"
disclaimerSections={disclaimerSections}
theme="light" // or "dark"
/>
);
};Controlling Modal Visibility
The modal has three visibility states:
- First Visit: Shows automatically (controlled by
defaultOpen, defaults totrue) - After Acceptance: Hidden (stored in localStorage)
- Manual Control: Can be reset using the
reset()function
const MyComponent = () => {
const { isOpen, onClose, reset } = useDisclaimer('unique-id');
// Use this to manually show the modal again
const handleReset = () => {
reset(); // Clears localStorage and shows the modal
};
return (
<>
<DisclaimerModal
isOpen={isOpen}
onClose={onClose}
title="Agreement Required"
disclaimerSections={[...]}
/>
{/* Optional: Button to show the modal again */}
<button onClick={handleReset}>Show Agreement</button>
</>
);
};Next.js Usage
Works in both .jsx/.js and .tsx/.ts pages:
JavaScript Pages (pages/example.js)
import { DisclaimerProvider } from '@citiwave/im-disclaimer-modal';
export default function ExamplePage() {
return (
<>
<h1>Your Content</h1>
<DisclaimerProvider
id="page-disclaimer"
title="Important Notice"
disclaimerSections={[
{
title: 'Terms',
required: true,
content: <div>Terms content...</div>
}
]}
/>
</>
);
}TypeScript Pages (pages/example.tsx)
import type { NextPage } from 'next';
import { DisclaimerProvider, type DisclaimerSection } from '@citiwave/im-disclaimer-modal';
const ExamplePage: NextPage = () => {
const sections: DisclaimerSection[] = [
{
title: 'Terms',
required: true,
content: <div>Terms content...</div>
}
];
return (
<>
<h1>Your Content</h1>
<DisclaimerProvider
id="page-disclaimer"
title="Important Notice"
disclaimerSections={sections}
/>
</>
);
};
export default ExamplePage;Best Practices for Both
- Unique IDs: Always use unique IDs to prevent conflicts
// Good
<DisclaimerProvider id="privacy-notice" ... />
<DisclaimerProvider id="terms-notice" ... />
// Bad
<DisclaimerProvider id="disclaimer" ... />
<DisclaimerProvider id="disclaimer" ... />- Import Styles: Don't forget to import the CSS file
import '@citiwave/im-disclaimer-modal/style.css';- Component Placement: Place the provider high in your component tree
// Good - In _app.js/tsx or layout component
function App() {
return (
<>
<DisclaimerProvider ... />
{/* Other components */}
</>
);
}
// Also Good - In specific pages when needed
function TermsPage() {
return (
<>
<DisclaimerProvider ... />
{/* Page content */}
</>
);
}Usage in Next.js
In Pages or Components
The modal works seamlessly in both Next.js pages and components. For pages, make sure to use the DisclaimerProvider:
// pages/your-page.tsx
import { DisclaimerProvider } from '@citiwave/im-disclaimer-modal';
import '@citiwave/im-disclaimer-modal/style.css';
export default function YourPage() {
return (
<>
{/* Your page content */}
<DisclaimerProvider
id="page-disclaimer"
title="Important Notice"
disclaimerSections={[
{
title: 'Terms of Service',
required: true,
content: <div>Your terms content here...</div>
}
]}
/>
</>
);
}Best Practices
- Place in Layout: For site-wide disclaimers, place the DisclaimerProvider in your layout component:
// components/Layout.tsx
import { DisclaimerProvider } from '@citiwave/im-disclaimer-modal';
export default function Layout({ children }) {
return (
<>
{children}
<DisclaimerProvider
id="site-disclaimer"
title="Site Terms"
disclaimerSections={[...]}
/>
</>
);
}- Page-Specific Disclaimers: For page-specific disclaimers, place the DisclaimerProvider at the page level:
// pages/sensitive-page.tsx
import { DisclaimerProvider } from '@citiwave/im-disclaimer-modal';
export default function SensitivePage() {
return (
<div>
<h1>Sensitive Content</h1>
<DisclaimerProvider
id="sensitive-content-disclaimer"
title="Content Warning"
disclaimerSections={[...]}
/>
</div>
);
}- Unique IDs: Always use unique IDs for different disclaimers to prevent conflicts:
// Good
<DisclaimerProvider id="privacy-notice" ... />
<DisclaimerProvider id="terms-notice" ... />
// Bad
<DisclaimerProvider id="disclaimer" ... />
<DisclaimerProvider id="disclaimer" ... />Usage in TypeScript
The package is fully typed and works seamlessly with TypeScript. Here's how to use it in .tsx files:
import { DisclaimerProvider, DisclaimerSection } from '@citiwave/im-disclaimer-modal';
import '@citiwave/im-disclaimer-modal/style.css';
const YourPage: React.FC = () => {
const disclaimerSections: DisclaimerSection[] = [
{
title: 'Terms of Service',
required: true,
content: <div>Your terms content here...</div>
}
];
return (
<>
<h1>Your Page Content</h1>
<DisclaimerProvider
id="page-disclaimer"
title="Important Notice"
disclaimerSections={disclaimerSections}
/>
</>
);
};
export default YourPage;With Next.js Pages
// pages/your-page.tsx
import type { NextPage } from 'next';
import { DisclaimerProvider, DisclaimerSection } from '@citiwave/im-disclaimer-modal';
import '@citiwave/im-disclaimer-modal/style.css';
const YourPage: NextPage = () => {
const disclaimerSections: DisclaimerSection[] = [
{
title: 'Terms of Service',
required: true,
content: <div>Your terms content here...</div>
}
];
return (
<>
<h1>Your Page Content</h1>
<DisclaimerProvider
id="page-disclaimer"
title="Important Notice"
disclaimerSections={disclaimerSections}
/>
</>
);
};
export default YourPage;Common Mistakes
Incorrect Usage: Using as a Constructor
// This is WRONG! Don't do this:
const modal = new DisclaimerModal({
title: "Agreement Required",
disclaimerSections: [...],
theme: "light"
});Correct Usage: Using as a React Component
// This is the correct way:
import { DisclaimerModal, useDisclaimer } from '@citiwave/im-disclaimer-modal';
import '@citiwave/im-disclaimer-modal/style.css'; // Don't forget to import the styles!
const MyComponent = () => {
const { isOpen, onClose } = useDisclaimer('unique-id');
return (
<DisclaimerModal
isOpen={isOpen}
onClose={onClose}
title="Agreement Required"
disclaimerSections={[...]}
theme="light"
/>
);
};Remember:
- DisclaimerModal is a React component, use it with JSX syntax (
<DisclaimerModal />) - Always import and use the
useDisclaimerhook to manage the modal's state - Don't forget to import the CSS file
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| isOpen | boolean | false | Controls the visibility of the modal |
| onClose | function | - | Callback function when modal is closed |
| title | string | - | Modal title |
| disclaimerSections | array | [] | Array of sections to display |
| theme | string | 'light' | Theme of the modal ('light' or 'dark') |
useDisclaimer Hook Props
| Prop | Type | Default | Description |
|---|---|---|---|
| id | string | - | Unique identifier for the disclaimer |
| options.storagePrefix | string | - | Prefix for local storage key |
| options.defaultOpen | boolean | true | Initial open state |
License
MIT