@mxweb/react-polyfill
v0.0.1
Published
React polyfills for modern hooks in older React versions (16.8+)
Downloads
99
Maintainers
Readme
@mxweb/react-polyfill
React polyfills for modern hooks in older React versions (16.8+)
Features
- 🔄 useSyncExternalStore - Polyfill for React 18's useSyncExternalStore hook
- 🎨 useIsomorphicLayoutEffect - SSR-safe useLayoutEffect alternative
- 🚀 React Module Augmentation - Use polyfills directly from 'react' module
- 📦 Tree-shakeable - Only bundle what you use
- 🔒 Type-safe - Full TypeScript support
- 🪶 Lightweight - Minimal bundle size impact
Installation
npm install @mxweb/react-polyfill
# or
yarn add @mxweb/react-polyfill
# or
pnpm add @mxweb/react-polyfillUsage
Option 1: Direct Import (Recommended)
Import hooks directly from the package:
import { useIsomorphicLayoutEffect, useSyncExternalStore } from '@mxweb/react-polyfill';
function MyComponent() {
// Use isomorphic layout effect (safe for SSR)
useIsomorphicLayoutEffect(() => {
const height = element.offsetHeight;
setHeight(height);
}, [element]);
// Use sync external store
const width = useSyncExternalStore(
(callback) => {
window.addEventListener('resize', callback);
return () => window.removeEventListener('resize', callback);
},
() => window.innerWidth,
() => 0 // Server-side default
);
return <div>Width: {width}</div>;
}Option 2: React Module Augmentation (Recommended for Global Setup)
Import the augmentation module once in your app's entry point, then use hooks from 'react' everywhere:
Setup in Entry Point
Choose the appropriate setup based on your framework:
React (Vite/CRA):
// src/main.tsx or src/index.tsx
import '@mxweb/react-polyfill/react';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);Next.js:
// pages/_app.tsx or app/layout.tsx
import '@mxweb/react-polyfill/react';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}Remix:
// app/root.tsx
import '@mxweb/react-polyfill/react';
// ... rest of your root componentOr create a dedicated setup file:
// src/setup.ts
import '@mxweb/react-polyfill/react';
// Then in your entry file:
// src/index.tsx
import './setup';
import React from 'react';
// ... rest of your codeUsage After Setup
After the global setup, you can import and use the polyfills from 'react' in any component:
// In any component file - no need to import the polyfill package
import { useIsomorphicLayoutEffect, useSyncExternalStore } from 'react';
function MyComponent() {
useIsomorphicLayoutEffect(() => {
// Your layout effect code
}, []);
const value = useSyncExternalStore(subscribe, getSnapshot);
return <div>{value}</div>;
}Benefits of Module Augmentation:
- ✅ No need to change imports in existing code
- ✅ Works seamlessly with libraries expecting React 18+ API
- ✅ Automatic fallback to native implementation in React 18+
- ✅ Full TypeScript support
API
useIsomorphicLayoutEffect
A hook that uses useLayoutEffect on the client and useEffect on the server.
useIsomorphicLayoutEffect(effect: EffectCallback, deps?: DependencyList): voidUse cases:
- DOM measurements
- Scroll position management
- Animation setup
- Any synchronous DOM manipulation that needs to happen before paint
Example:
import { useIsomorphicLayoutEffect } from '@mxweb/react-polyfill';
function Component() {
const ref = useRef<HTMLDivElement>(null);
useIsomorphicLayoutEffect(() => {
if (ref.current) {
const height = ref.current.offsetHeight;
console.log('Height:', height);
}
}, []);
return <div ref={ref}>Content</div>;
}useSyncExternalStore
A polyfill for React 18's useSyncExternalStore hook. Subscribes to an external store.
useSyncExternalStore<Snapshot>(
subscribe: (onStoreChange: () => void) => () => void,
getSnapshot: () => Snapshot,
getServerSnapshot?: () => Snapshot
): SnapshotParameters:
subscribe- Function that subscribes to the store and returns an unsubscribe functiongetSnapshot- Function that returns the current store valuegetServerSnapshot- Optional function that returns the server-side value
Example:
import { useSyncExternalStore } from '@mxweb/react-polyfill';
// Window width store
function useWindowWidth() {
return useSyncExternalStore(
(callback) => {
window.addEventListener('resize', callback);
return () => window.removeEventListener('resize', callback);
},
() => window.innerWidth,
() => 0 // Default for SSR
);
}
// Custom store
const store = {
listeners: new Set(),
value: 0,
subscribe(callback) {
this.listeners.add(callback);
return () => this.listeners.delete(callback);
},
getSnapshot() {
return this.value;
},
setValue(newValue) {
this.value = newValue;
this.listeners.forEach(listener => listener());
}
};
function Counter() {
const value = useSyncExternalStore(
(callback) => store.subscribe(callback),
() => store.getSnapshot(),
() => 0
);
return (
<div>
<p>Value: {value}</p>
<button onClick={() => store.setValue(value + 1)}>
Increment
</button>
</div>
);
}TypeScript
Full TypeScript support is included. When using React module augmentation, the types are automatically available:
// After importing '@mxweb/react-polyfill/react'
import { useIsomorphicLayoutEffect, useSyncExternalStore } from 'react';
// TypeScript knows about these hooks ✅React Version Compatibility
| React Version | useSyncExternalStore | useIsomorphicLayoutEffect | |---------------|---------------------|---------------------------| | 16.8 - 17.x | ✅ Polyfill | ✅ Available | | 18.0+ | ✅ Native | ✅ Available |
Bundle Size
- Core polyfills: ~1KB gzipped
- Tree-shakeable: Only bundle what you use
- Zero dependencies (except React peer dependency)
License
MIT © MXWeb Team
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
