Guide for understanding font loading in open-quran-view, including the v0.2.0+ static assets system.
open-quran-view uses a static assets system (v0.2.0+) to reliably load fonts and data files. This system pre-generates import.meta.url based URLs at build time, eliminating runtime path resolution issues.
The original implementation constructed URLs at runtime:
// ❌ Problematic approach (pre-v0.2.0)
const fontUrl = new URL("../data/fonts/hafs-v2/p1.woff2", import.meta.url).href;
const response = await fetch(fontUrl);
| Scenario | Behavior | Result |
|---|---|---|
| workspace: protocol | Vite intercepts requests during dev | ✅ Works locally |
| Published to npm | Files in node_modules are inaccessible |
❌ Fails |
| file:// URLs | Browsers block HTTP requests to local files | ❌ Fails |
Failed to load page: NetworkError: A network error occurred.
This occurred because browsers cannot make fetch() requests to file:// URLs from node_modules.
The v0.2.0 release introduced a static assets system that pre-generates all asset URLs at build time.
┌─────────────────────────────────────────────────────────────────┐
│ Build Time (generate-static-*) │
│ │
│ 1. Scan src/data/fonts/ for all font files │
│ 2. Scan src/data/pages/ and src/data/metadata/ │
│ 3. Generate TypeScript files with static URL constants │
│ 4. Output to src/core/static/ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Runtime (import.meta.url) │
│ │
│ 1. Consumer imports static assets │
│ 2. import.meta.url is resolved by the bundler │
│ 3. fetch() receives correct URL │
└─────────────────────────────────────────────────────────────────┘
Generated file: src/core/static/fonts.ts
export const staticFonts = {
"hafs-v2": {
1: new URL("../../data/fonts/hafs-v2/p1.woff2", import.meta.url).href,
2: new URL("../../data/fonts/hafs-v2/p2.woff2", import.meta.url).href,
// ... 604 entries
},
"hafs-v4": { /* ... */ },
"hafs-unicode": { /* ... */ },
} as const;
export function getFontUrl(layout: MushafLayout, page: number): string {
const pageStr = String(page);
return staticFonts[layout]?.[pageStr];
}
Generated file: src/core/static/data.ts
export const staticData = {
pages: {
"hafs-v2": new URL("../../data/pages/hafs-v2/pages.json", import.meta.url).href,
"hafs-v4": new URL("../../data/pages/hafs-v4/pages.json", import.meta.url).href,
"hafs-unicode": new URL("../../data/pages/hafs-unicode/pages.json", import.meta.url).href,
},
metadata: {
surahs: new URL("../../data/metadata/surahs.json", import.meta.url).href,
juz: new URL("../../data/metadata/juz.json", import.meta.url).href,
},
} as const;
export function getPagesUrl(layout: string): string {
return staticData.pages[layout];
}
export function getMetadataUrl(type: "surahs" | "juz"): string {
return staticData.metadata[type];
}
Static assets are generated by running the generation scripts:
# Generate static font URLs
yarn generate:static:fonts
# Generate static data URLs
yarn generate:static:data
# Generate both
yarn generate:static
# Or generate everything (metadata, pages, fonts, static assets)
yarn generate:all
The prepare script in package.json runs automatically:
{
"scripts": {
"prepare": "yarn generate:all && yarn build"
}
}
When the static assets are imported at runtime:
// In the consumer's application
import { getFontUrl } from 'open-quran-view/core/static/fonts';
const fontUrl = getFontUrl('hafs-v2', 1);
// Returns something like:
// - Vite dev: http://localhost:5173/node_modules/open-quran-view/dist/data/fonts/hafs-v2/p1.woff2
// - Production: /node_modules/open-quran-view/dist/data/fonts/hafs-v2/p1.woff2
The bundler (Vite, webpack, etc.) correctly resolves import.meta.url because the URLs were pre-generated with the correct relative paths.
After running yarn build, the static assets are included in the package:
dist/
├── core/
│ └── static/
│ ├── fonts.js # Static font URLs
│ └── data.js # Static data URLs
├── data/
│ ├── fonts/
│ │ └── hafs-v2/p1.woff2...
│ └── pages/
│ └── hafs-v2/pages.json
└── view/
├── react/index.js
└── web/index.js
No special configuration needed:
npm install open-quran-view
import { OpenQuranView } from 'open-quran-view/view';
function App() {
return <OpenQuranView page={1} mushafLayout="hafs-v2" />;
}
Required manual setup with Vite plugin (see [archived documentation]).
If you modify data files, regenerate static assets:
# After modifying src/data/fonts/ or src/data/pages/
yarn generate:static
yarn build