A fully featured, customizable, and accessible footnote management system for React applications. Provides automatic numbering, context-based management, and flexible rendering with TypeScript support for modern web development.
โจ Core Features
- ๐ข Automatic Numbering โ smart footnote numbering with context awareness
- ๐ Context Management โ centralized footnote state across components
- ๐ฏ Flexible Rendering โ customizable footnote references and lists
- โก TypeScript Support โ comprehensive type definitions included
- ๐จ Customizable Styling โ complete visual customization options
- โฟ Accessible โ ARIA support and semantic HTML structure
- ๐ Dynamic Registration โ footnotes register automatically when mounted
- ๐ฑ Responsive Design โ works seamlessly across all device sizes
๐ฆ Installation
npm install "@harshiniravikumar/react-footnote-system"
๐ง Main Components
FootnoteContext | Provider | Context provider for footnote management |
FootnoteReference | Component | Creates footnote references with automatic numbering |
FootnoteList | Component | Renders all registered footnotes in list format |
Footnote | Component | Standalone footnote component for advanced use cases |
๐ฏ Component Props
id | string | - | Unique identifier for the footnote (required) |
children | ReactNode | - | The footnote content |
className | string | '' | Additional CSS classes for the reference |
onClick | () => void | - | Custom click handler |
style | CSSProperties | {} | Inline styles for the reference |
renderReference | (number: number) => ReactNode | - | Custom reference renderer |
className | string | '' | CSS classes for the list container |
itemClassName | string | '' | CSS classes for footnote items |
title | string | "Footnotes" | Title for the footnote section |
showTitle | boolean | true | Whether to display the title |
orderBy | "appearance" | "alphabetical" | "appearance" | Footnote ordering method |
renderItem | (footnote: FootnoteData) => ReactNode | - | Custom footnote item renderer |
id | string | - | Unique identifier (required) |
content | string | ReactNode | - | Footnote content |
number | number | - | Manual footnote number override |
autoRegister | boolean | true | Automatically register with context |
startNumber | number | 1 | Starting number for footnote numbering |
numberingStyle | "numeric" | "roman" | "alpha" | "numeric" | Numbering style for footnotes |
allowDuplicates | boolean | false | Allow duplicate footnote IDs |
resetOnUnmount | boolean | false | Reset footnotes when context unmounts |
maxFootnotes | number | undefined | Maximum number of footnotes allowed |
๐จ Theme Configuration
Customize the visual appearance of your footnotes:
const footnoteTheme = {
reference: {
color: "#007bff",
fontSize: "0.8em",
verticalAlign: "super",
textDecoration: "none",
padding: "0 2px",
borderRadius: "2px",
backgroundColor: "transparent"
},
list: {
borderTop: "1px solid #e0e0e0",
marginTop: "2rem",
paddingTop: "1rem",
fontSize: "0.9em"
},
item: {
marginBottom: "0.5rem",
lineHeight: "1.4",
color: "#666"
},
title: {
fontSize: "1.1em",
fontWeight: "bold",
marginBottom: "1rem",
color: "#333"
}
};
๐ผ๏ธ Basic Usage
import React from 'react';
import {
FootnoteContext,
FootnoteReference,
FootnoteList
} from "@your-username/react-footnote-system";
function BasicExample() {
return (
<FootnoteContext>
<article>
<h1>Article Title</h1>
<p>
This is some content with a footnote
<FootnoteReference id="note1">
This appears as footnote #1 at the bottom of the page.
</FootnoteReference>
and more text continues here.
</p>
<p>
Another paragraph with another reference
<FootnoteReference id="note2">
This becomes footnote #2 with automatic numbering.
</FootnoteReference>
in the middle of the sentence.
</p>
<FootnoteList />
</article>
</FootnoteContext>
);
}
๐ฎ Advanced Usage with All Features
import React, { useRef, useContext } from 'react';
import {
FootnoteContext,
FootnoteReference,
FootnoteList,
useFootnoteContext
} from "@your-username/react-footnote-system";
function AdvancedExample() {
const footnoteContext = useFootnoteContext();
const handleFootnoteClick = (id: string) => {
console.log('Footnote clicked:', id);
};
return (
<FootnoteContext
options={{
startNumber: 1,
numberingStyle: "numeric",
allowDuplicates: false,
resetOnUnmount: true,
maxFootnotes: 50
}}
theme={{
reference: {
color: "#0066cc",
backgroundColor: "#f0f8ff",
borderRadius: "4px",
padding: "2px 4px"
},
list: {
borderTop: "2px solid #0066cc",
backgroundColor: "#fafafa"
}
}}
>
<main>
<section>
<h1>Research Paper</h1>
<p>
Recent studies show significant findings
<FootnoteReference
id="study-2024"
onClick={() => handleFootnoteClick("study-2024")}
className="custom-reference"
>
Smith, J., & Johnson, A. (2024). "Advanced Research Methods in Modern Science."
<em>Journal of Scientific Discovery</em>, 42(3), 123-145.
DOI: 10.1000/journal.2024.123
</FootnoteReference>
that support our hypothesis.
</p>
<p>
The methodology employed
<FootnoteReference id="methodology">
Our research methodology followed the guidelines established by
the International Research Standards Committee (IRSC).
</FootnoteReference>
ensures reliable results across multiple scenarios.
</p>
</section>
<footer>
<FootnoteList
title="References & Notes"
className="research-footnotes"
itemClassName="footnote-item"
showTitle={true}
orderBy="appearance"
renderItem={(footnote) => (
<div className="custom-footnote">
<span className="footnote-number">{footnote.number}.</span>
<span className="footnote-content">{footnote.content}</span>
</div>
)}
/>
</footer>
</main>
</FootnoteContext>
);
}
๐ฑ Multiple Contexts & Sections
function MultiSectionDocument() {
return (
<div>
{/* Chapter 1 with independent footnotes */}
<FootnoteContext options={{ startNumber: 1 }}>
<section>
<h2>Chapter 1: Introduction</h2>
<p>
Content with footnotes
<FootnoteReference id="ch1-intro">
Introduction footnote content here.
</FootnoteReference>
specific to this chapter.
</p>
<FootnoteList title="Chapter 1 Notes" />
</section>
</FootnoteContext>
<FootnoteContext options={{ startNumber: 1, numberingStyle: "roman" }}>
<section>
<h2>Chapter 2: Methodology</h2>
<p>
Different chapter content
<FootnoteReference id="ch2-method">
Methodology footnote with roman numerals.
</FootnoteReference>
with separate footnote numbering.
</p>
<FootnoteList title="Chapter 2 Notes" />
</section>
</FootnoteContext>
</div>
);
}
๐ฌ Custom Renderers & Animations
function CustomFootnoteExample() {
return (
<FootnoteContext>
<div>
<p>
Text with custom reference
<FootnoteReference
id="custom"
renderReference={(number) => (
<span className="custom-ref">
[{number}]
</span>
)}
>
Custom styled footnote content
</FootnoteReference>
</p>
<FootnoteList
renderItem={(footnote) => (
<div className="animated-footnote">
<strong>{footnote.number}.</strong>
<span>{footnote.content}</span>
</div>
)}
/>
</div>
</FootnoteContext>
);
}
๐ Callbacks & Events
onFootnoteRegister | (id: string, content: ReactNode) => void | Called when footnote is registered |
onFootnoteUnregister | (id: string) => void | Called when footnote is unregistered |
onFootnoteClick | (id: string, number: number) => void | Called when footnote reference is clicked |
onListRender | (footnotes: FootnoteData[]) => void | Called when footnote list renders |
onMaxFootnotesReached | () => void | Called when max footnotes limit is reached |
Access footnote context methods and state:
footnotes | FootnoteData[] | Array of all registered footnotes |
register | (id: string, content: ReactNode) => void | Register a new footnote |
unregister | (id: string) => void | Unregister a footnote |
getFootnote | (id: string) => FootnoteData | undefined | Get footnote by ID |
getFootnoteNumber | (id: string) => number | undefined | Get footnote number by ID |
reset | () => void | Clear all footnotes |
count | number | Total number of registered footnotes |
import { useFootnoteContext } from "@your-username/react-footnote-system";
function FootnoteStats() {
const { footnotes, count, reset } = useFootnoteContext();
return (
<div>
<p>Total footnotes: {count}</p>
<button onClick={reset}>Clear All Footnotes</button>
</div>
);
}
๐ฏ Real-World Examples
Academic Paper
<FootnoteContext options={{ numberingStyle: "numeric" }}>
<article className="academic-paper">
<h1>The Impact of Climate Change on Marine Ecosystems</h1>
<p>
Recent oceanographic studies
<FootnoteReference id="ocean-study-2024">
Martinez, C., et al. (2024). "Ocean Temperature Variations and Marine Biodiversity."
<em>Marine Biology Quarterly</em>, 78(2), 45-67.
</FootnoteReference>
indicate significant temperature variations in coastal waters.
</p>
<p>
These findings corroborate earlier research
<FootnoteReference id="previous-research">
See Thompson, R. (2022) and Williams, S. (2023) for comprehensive
reviews of historical temperature data.
</FootnoteReference>
from the past decade.
</p>
<FootnoteList title="References" className="academic-references" />
</article>
</FootnoteContext>
Blog Post with Explanatory Notes
<FootnoteContext options={{ numberingStyle: "alpha" }}>
<article className="blog-post">
<h1>Understanding React Hooks</h1>
<p>
React Hooks revolutionized functional components
<FootnoteReference id="hooks-intro">
Hooks were introduced in React 16.8 (February 2019) and allow you to
use state and other React features in functional components.
</FootnoteReference>
by enabling state management without classes.
</p>
<p>
The useState hook
<FootnoteReference id="usestate-example">
<code>const [state, setState] = useState(initialValue)</code> is the
most commonly used hook for managing component state.
</FootnoteReference>
is fundamental to modern React development.
</p>
<FootnoteList
title="Additional Information"
className="blog-footnotes"
showTitle={true}
/>
</article>
</FootnoteContext>
Legal Document with Roman Numerals
<FootnoteContext options={{ numberingStyle: "roman" }}>
<div className="legal-document">
<h1>Terms of Service</h1>
<section>
<h2>Section 1: Definitions</h2>
<p>
"Service" refers to the platform
<FootnoteReference id="service-definition">
As defined under applicable consumer protection laws and regulations
in the jurisdiction where the service is provided.
</FootnoteReference>
provided by the Company.
</p>
</section>
<FootnoteList
title="Legal References"
className="legal-footnotes"
itemClassName="legal-footnote-item"
/>
</div>
</FootnoteContext>
๐จ Custom Styling Examples
Modern Glass Effect
.modern-footnotes .footnote-reference {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
padding: 2px 6px;
color: #007bff;
text-decoration: none;
transition: all 0.2s ease;
}
.modern-footnotes .footnote-reference:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-1px);
}
.modern-footnotes .footnote-list {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
border-radius: 12px;
padding: 1.5rem;
margin-top: 2rem;
}
Academic Style
.academic-footnotes .footnote-reference {
color: #d32f2f;
font-weight: 500;
vertical-align: super;
font-size: 0.75em;
text-decoration: none;
border-bottom: 1px dotted #d32f2f;
}
.academic-footnotes .footnote-list {
border-top: 2px solid #333;
margin-top: 3rem;
padding-top: 1rem;
font-family: 'Times New Roman', serif;
}
.academic-footnotes .footnote-item {
text-indent: -1em;
padding-left: 1em;
margin-bottom: 0.8rem;
}
๐ฑ Responsive Behavior
function ResponsiveFootnotes() {
return (
<FootnoteContext>
<div className="responsive-content">
<p>
Mobile-optimized footnotes
<FootnoteReference
id="mobile-note"
className="responsive-reference"
>
On mobile devices, footnotes adapt to smaller screens with
optimized touch targets and readable font sizes.
</FootnoteReference>
work seamlessly across devices.
</p>
<FootnoteList className="responsive-footnote-list" />
</div>
</FootnoteContext>
);
}
@media (max-width: 768px) {
.responsive-reference {
font-size: 0.9em !important;
padding: 4px 6px !important;
}
.responsive-footnote-list {
font-size: 0.85em;
padding: 1rem 0.5rem;
}
}
โฟ Accessibility Features
- ARIA Labels: Proper labeling for screen readers
- Keyboard Navigation: Tab and Enter key support
- Semantic HTML: Uses appropriate HTML elements
- High Contrast: Works with high contrast modes
- Screen Reader Support: Announces footnote numbers and content
- Focus Management: Proper focus indicators and management
<FootnoteReference
id="accessible-note"
aria-label="Footnote reference 1"
role="doc-noteref"
>
Accessible footnote content
</FootnoteReference>
๐ Performance Features
- Lazy Registration: Footnotes register only when needed
- Efficient Re-renders: Optimized with React.memo and context optimization
- Memory Management: Automatic cleanup on unmount
- Minimal Bundle Size: Tree-shakeable components
- No Dependencies: Pure React implementation
๐ TypeScript Support
Comprehensive TypeScript definitions included:
import type {
Footnotes,
FootnoteProps,
FootnoteContextType,
FootnoteRefProps,
FootnoteListProps,
FootnoteData,
FootnoteOptions,
FootnoteTheme
} from "@your-username/react-footnote-system";
interface CustomFootnoteData {
id: string;
content: string;
category: 'reference' | 'explanation' | 'source';
}
const MyComponent: React.FC = () => {
const [footnotes, setFootnotes] = useState<CustomFootnoteData[]>([]);
return (
<FootnoteContext>
{/* Your footnote implementation */}
</FootnoteContext>
);
};
๐ง Configuration Examples
<FootnoteContext options={{ numberingStyle: "numeric" }}>
{}
</FootnoteContext>
<FootnoteContext options={{ numberingStyle: "roman" }}>
{}
</FootnoteContext>
<FootnoteContext options={{ numberingStyle: "alpha" }}>
{}
</FootnoteContext>
Custom Starting Number
<FootnoteContext options={{ startNumber: 10 }}>
{}
</FootnoteContext>
๐ช Interactive Examples
function InteractiveFootnotes() {
const [selectedFootnote, setSelectedFootnote] = useState<string | null>(null);
return (
<FootnoteContext>
<div>
<p>
Click this footnote
<FootnoteReference
id="interactive"
onClick={() => setSelectedFootnote("interactive")}
>
This footnote has custom click behavior and can trigger actions
in your application.
</FootnoteReference>
to see custom behavior.
</p>
{selectedFootnote && (
<div className="footnote-popup">
<p>You clicked footnote: {selectedFootnote}</p>
<button onClick={() => setSelectedFootnote(null)}>Close</button>
</div>
)}
<FootnoteList />
</div>
</FootnoteContext>
);
}
function DynamicFootnotes() {
const { register, unregister, footnotes, reset } = useFootnoteContext();
const addDynamicFootnote = () => {
const id = `dynamic-${Date.now()}`;
register(id, `Dynamic footnote added at ${new Date().toLocaleTimeString()}`);
};
return (
<div>
<button onClick={addDynamicFootnote}>Add Footnote</button>
<button onClick={reset}>Clear All</button>
<p>Current footnotes: {footnotes.length}</p>
<FootnoteList />
</div>
);
}
๐ฎ Integration with Other Libraries
With Markdown Processors
import ReactMarkdown from 'react-markdown';
function MarkdownWithFootnotes({ content }: { content: string }) {
return (
<FootnoteContext>
<ReactMarkdown
components={{
sup: ({ children }) => (
<FootnoteReference id={`md-${children}`}>
Markdown footnote content
</FootnoteReference>
)
}}
>
{content}
</ReactMarkdown>
<FootnoteList />
</FootnoteContext>
);
}
With Content Management Systems
function CMSContent({ content }: { content: any[] }) {
return (
<FootnoteContext>
{content.map((block, index) => {
if (block.type === 'footnote') {
return (
<FootnoteReference key={index} id={block.id}>
{block.content}
</FootnoteReference>
);
}
return <div key={index}>{block.content}</div>;
})}
<FootnoteList />
</FootnoteContext>
);
}
๐งช Testing Support
import { render, screen } from '@testing-library/react';
import { FootnoteContext, FootnoteReference, FootnoteList } from 'your-library';
test('footnotes render correctly', () => {
render(
<FootnoteContext>
<FootnoteReference id="test">Test footnote</FootnoteReference>
<FootnoteList />
</FootnoteContext>
);
expect(screen.getByText('1')).toBeInTheDocument();
expect(screen.getByText('Test footnote')).toBeInTheDocument();
});
๐ License
MIT License - feel free to use in your projects!