react-e-ink
A React component library that simulates a realistic e-ink display. Wraps any React app with grayscale rendering, content ghosting, stepped scrolling, WebGL surface textures, and a full/partial refresh cycle.
Install
pnpm add react-e-ink
Peer dependencies: react >= 19, react-dom >= 19, motion >= 12.
Quick start
import { EInkProvider, EInkDisplay } from "react-e-ink";
import "react-e-ink/styles.css";
function EInkShell({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
return (
<EInkProvider pathname={pathname}>
<EInkDisplay pathname={pathname}>{children}</EInkDisplay>
</EInkProvider>
);
}
The engine is framework-agnostic — pass pathname from whatever router you use (Next.js, React Router, TanStack Router, etc.).
The package ships TypeScript source (no build step). Configure your bundler to transpile it:
export default {
transpilePackages: ["react-e-ink"],
};
Components
<EInkProvider>
Context provider that manages the refresh cycle and ghost state.
pathname | string | Current route path from your router |
children | node | App content |
<EInkDisplay>
The main visual wrapper. Renders the e-ink rendering pipeline around your content.
pathname | string | Must match the value passed to provider |
children | node | Page content |
renderOverlay | () => node | Optional. Render custom UI (status bar, decorations) inside the fixed overlay layer |
<EInkDisplay
pathname={pathname}
renderOverlay={() => (
<>
<MyStatusBar />
<MyDecorations />
</>
)}
>
{children}
</EInkDisplay>
Hook: useEInk()
Access the engine state from any component inside the provider. Use this to build custom UI like status bars, refresh buttons, or debug overlays.
const {
refreshMode,
ghostIntensity,
isInitialLoad,
refreshProgress,
forceRefresh,
addProgress,
} = useEInk();
Refresh cycle
- Partial refresh on each navigation — fast, but accumulates ghosting.
- Full refresh when progress reaches 1.0 — clears all ghosts and artifacts.
- Navigation adds ~14% progress per page change (~7 navigations per full refresh).
- Scrolling adds small increments of progress per step.
- Call
forceRefresh() to trigger a full refresh at any time.
Dark mode
Add the eink-dark class to <html> to invert all colors. The CSS variables flip automatically.
Customization
Fonts
:root {
--eink-font-body: "Literata", Georgia, serif;
--eink-font-mono: "JetBrains Mono", "Courier New", monospace;
}
Colors
:root {
--eink-paper: #f2ecdb;
--eink-black: #1a1610;
--eink-dark: #332d24;
--eink-mid: #74694f;
--eink-light: #a89b7a;
--eink-ghost: #d1c9ae;
}
Accessibility
- Animations respect
prefers-reduced-motion.
- External links automatically get
target="_blank" and rel="noopener noreferrer".
License
MIT