Reference for
@open-quran-view/viewReact and Web Component views.
| Import | Target | Description |
|---|---|---|
@open-quran-view/view |
React component (default) | import { OpenQuranView } |
@open-quran-view/view/react |
React component (explicit) | Same as default |
@open-quran-view/view/web |
Web Component | import { registerOpenQuranView } |
import { OpenQuranView } from '@open-quran-view/view';
function App() {
return (
<OpenQuranView
page={1}
mushafLayout="hafs-v2"
width={600}
height={850}
theme="light"
onPageChange={(page) => console.log('Page:', page)}
onLoad={(layout) => console.log('Loaded:', layout)}
onWordClick={(word) => console.log('Word:', word)}
/>
);
}
| Prop | Type | Default | Description |
|---|---|---|---|
page |
number | 1 |
Page number (1-604) |
width |
number | (optional) | Component width in pixels. If provided alone, height is derived from the mushaf ratio (unless ratio={false}) |
height |
number | (optional) | Component height in pixels. If provided alone, width is derived from the mushaf ratio (unless ratio={false}) |
ratio |
boolean \| number |
true |
Aspect ratio behavior. true (default) uses mushaf ratio (0.7) to derive missing dimension. false fills container dimensions without ratio derivation. A number overrides the ratio value (e.g., 0.8) |
fit |
"width" \| "height" |
"width" |
Which dimension to fill when neither width nor height is provided. "width" (default) fills container width and derives height. "height" fills container height and derives width. |
theme |
"light" \| "dark" |
"light" |
Color theme |
mushafLayout |
"hafs-v2" \| "hafs-v4" \| "hafs-unicode" |
"hafs-v2" |
Mushaf layout |
onPageChange |
(page: number) => void |
- | Called when page changes |
onLoad |
(layout: PageLayout) => void |
- | Called when page loads |
onWordClick |
(word: WordInfo) => void |
- | Called when word is clicked |
highlightedWords |
WordLocation[] |
[] |
List of words to highlight |
highlightedVerse |
{ surah: number, verse: number } \| null |
null |
Verse to highlight (line background) |
wordHighlightColor |
string |
"rgba(255, 215, 0, 0.5)" |
Word highlight color |
verseHighlightColor |
string |
"rgba(135, 206, 250, 0.25)" |
Verse highlight color |
className |
string | - | CSS class for container |
ratio value |
Width provided | Height provided | fit |
Result |
|---|---|---|---|---|
true (default) |
no | no | "width" (default) |
Fills container width, derives height (width / 0.7) |
true (default) |
no | no | "height" |
Fills container height, derives width (height * 0.7) |
true (default) |
yes | no | any | Uses width, derives height (width / 0.7) |
true (default) |
no | yes | any | Uses height, derives width (height * 0.7) |
true (default) |
yes | yes | any | Uses both explicitly |
false |
any | any | any | No ratio derivation; uses explicit dims or container dims |
0.8 (number) |
yes | no | any | Uses width, derives height (width / 0.8) |
0.8 (number) |
no | yes | any | Uses height, derives width (height * 0.8) |
The component warns in development mode when props conflict:
| Conflicting Props | Warning Message |
|---|---|
Both width & height with fit |
"fit" prop will be ignored |
fit="height" with width (no height) |
"fit="height"" is ignored, using width |
fit="width" with height (no width) |
"fit="width"" is ignored, using height |
ratio={false} with fit |
"fit" prop is ignored when ratio={false} |
fullscreen with width or height |
Explicit dims are ignored in fullscreen |
The font size is calculated to ensure text fits within both width and height constraints:
fontSizeFromHeight = lineHeight / 1.5
fontSizeFromWidth = availableWidth / (avgCharsPerLine * 0.5)
fontSize = min(fontSizeFromHeight, fontSizeFromWidth)
This approach ensures:
type WordInfo = {
id: number;
surahNumber?: number;
ayahNumber?: number;
};
The React view includes built-in navigation controls:
import { registerOpenQuranView } from '@open-quran-view/view/web';
registerOpenQuranView();
<open-quran-view
page="1"
mushaf-layout="hafs-v2"
width="600"
height="850"
theme="light"
ratio="true"
fit="width"
></open-quran-view>
| Attribute | Type | Default | Description |
|---|---|---|---|
page |
string | "1" |
Page number (1-604) |
mushaf-layout |
string | "hafs-v2" |
Mushaf layout |
width |
string | (optional) | Component width in pixels. If provided alone, height is derived from the mushaf ratio |
height |
string | (optional) | Component height in pixels. If provided alone, width is derived from the mushaf ratio |
ratio |
string | "true" |
Aspect ratio behavior. "true" (default) uses mushaf ratio. "false" disables derivation. A number (e.g., "0.8") overrides the ratio |
fit |
string | "width" |
Which dimension to fill when neither width nor height provided. "width" fills width and derives height. "height" fills height and derives width |
theme |
string | "light" |
Color theme (light or dark) |
highlighted-words |
string | undefined |
JSON string of WordLocation[] |
highlighted-verse |
string | undefined |
JSON string of { surah: number, verse: number } |
word-highlight-color |
string | "rgba(255, 215, 0, 0.5)" |
Word highlight color |
verse-highlight-color |
string | "rgba(135, 206, 250, 0.25)" |
Verse highlight color |
| Event | Detail | Description |
|---|---|---|
load |
PageLayout |
Fired when page loads |
pagechange |
{ page: number } |
Fired when page changes |
wordclick |
WordInfo |
Fired when a word is clicked |
const viewer = document.querySelector('open-quran-view');
viewer.addEventListener('wordclick', (e: CustomEvent) => {
console.log('Word clicked:', e.detail);
});
const viewer = document.querySelector('open-quran-view') as OpenQuranView;
// Navigate to page
viewer.page = 10;
viewer.goToPage(10);
// Get current page
console.log(viewer.page);
// Change layout
viewer.setAttribute('mushaf-layout', 'hafs-v4');
// Change theme
viewer.setAttribute('theme', 'dark');
// Change dimensions
viewer.setAttribute('width', '800');
viewer.setAttribute('height', '1000');
// Highlighting (v0.4.0+)
viewer.wordsHighlight = [{ surah: 1, verse: 1, position: 1 }];
viewer.verseHighlight = { surah: 1, verse: 1 };
viewer.wordHighlightColorOverride = 'rgba(255, 215, 0, 0.5)';
viewer.verseHighlightColorOverride = 'rgba(135, 206, 250, 0.25)';
| Feature | React View | Web Component |
|---|---|---|
| Framework | React | Vanilla JS / Any |
| Bundle Size | ~18KB | ~19KB |
| Custom Styling | CSS-in-JS, classes | Shadow DOM, encapsulated |
| State Management | React hooks | Internal state |
| Event Handling | React props | Custom events |
| Accessibility | Keyboard + ARIA | Built-in |
| Theming | Prop-based | Attribute-based |
export type OpenQuranViewProps = {
page?: number;
width?: number;
height?: number;
ratio?: boolean | number;
fit?: "width" | "height";
theme?: "light" | "dark";
mushafLayout?: "hafs-v2" | "hafs-v4" | "hafs-unicode";
onPageChange?: (page: number) => void;
onLoad?: (layout: PageLayout) => void;
onWordClick?: (word: WordInfo) => void;
highlightedWords?: WordLocation[];
highlightedVerse?: { surah: number; verse: number } | null;
wordHighlightColor?: string;
verseHighlightColor?: string;
className?: string;
};
export type WordLocation = {
surah: number;
verse: number;
position: number;
};
export type OpenQuranViewProps = {
page?: string;
mushafLayout?: "hafs-v2" | "hafs-v4" | "hafs-unicode";
width?: string;
height?: string;
theme?: "light" | "dark";
};
export class OpenQuranView extends HTMLElement {
page: number;
mushafLayoutAttr: "hafs-v2" | "hafs-v4" | "hafs-unicode";
wordsHighlight: WordLocation[];
verseHighlight: { surah: number; verse: number } | null;
wordHighlightColorOverride: string;
verseHighlightColorOverride: string;
goToPage(page: number): void;
}
export function registerOpenQuranView(): void;
Both views depend on internal core modules:
import {
loadPage,
loadFont,
surahNumberToFontCode,
createLayoutCalculator,
type MushafLayout,
type PageLayout,
} from "../../core";
Fonts are loaded using the static asset system (v0.2.0+):
import { getFontUrl } from 'open-quran-view/core/static/fonts';
const fontUrl = getFontUrl('hafs-v2', 1);
See Static Assets Architecture for details.
src/view/
├── react/
│ └── index.tsx # OpenQuranView component
└── web/
└── index.ts # OpenQuranView + registerOpenQuranView
import { OpenQuranView } from '@open-quran-view/view';
function CustomQuranView() {
return (
<div className="quran-container">
<OpenQuranView
page={1}
mushafLayout="hafs-v2"
width={600}
height={850}
theme="dark"
onWordClick={(word) => {
console.log(`Surah ${word.surahNumber}:${word.ayahNumber}`);
}}
className="my-quran-view"
style=
/>
</div>
);
}
<!DOCTYPE html>
<html>
<head>
<script type="module">
import { registerOpenQuranView } from './dist/view/web/index.js';
registerOpenQuranView();
</script>
</head>
<body>
<open-quran-view
id="my-viewer"
page="1"
mushaf-layout="hafs-v2"
width="600"
height="850"
theme="light"
></open-quran-view>
<script>
const viewer = document.getElementById('my-viewer');
// Listen for word clicks
viewer.addEventListener('wordclick', (e) => {
console.log('Clicked:', e.detail);
});
// Listen for page changes
viewer.addEventListener('pagechange', (e) => {
console.log('Page changed to:', e.detail.page);
});
// Navigate after 3 seconds
setTimeout(() => {
viewer.page = 5;
}, 3000);
</script>
</body>
</html>
<template>
<open-quran-view
ref="viewer"
page="1"
mushaf-layout="hafs-v2"
theme="light"
@load="handleLoad"
@wordclick="handleWordClick"
/>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import { registerOpenQuranView } from '@open-quran-view/view/web';
registerOpenQuranView();
const viewer = ref(null);
const handleLoad = (e) => {
console.log('Layout loaded:', e.detail);
};
const handleWordClick = (e) => {
console.log('Word:', e.detail);
};
onMounted(() => {
viewer.value.page = 10;
});
</script>
| Element | Color |
|---|---|
| Background | #fafafa |
| Text | #34495e |
| Surah Name | #2c3e50 |
| Navigation | rgba(255,255,255,0.9) |
| Buttons | #667eea |
| Element | Color |
|---|---|
| Background | #1a1a2e |
| Text | #fff |
| Surah Name | #fff |
| Navigation | rgba(0,0,0,0.5) |
| Buttons | #333 |
Fonts are loaded dynamically based on the mushafLayout prop/attribute using the static asset system:
import { getFontUrl } from 'open-quran-view/core/static/fonts';
const fontFace = new FontFace("QuranFont", `url(${fontUrl})`);
await fontFace.load();
document.fonts.add(fontFace);
Fonts are managed internally based on the selected layout.