Overview
Open Mushaf Native supports multiple authentic Quranic recitation traditions (Riwayas), allowing users to read the Quran in their preferred Riwaya with the appropriate Mushaf layout.
Supported Riwayas
The app currently supports two major Riwayas:
Hafs عن عاصم The most widely used Riwaya, standard in most Muslim-majority countries
Warsh عن نافع Common in North and West Africa, particularly in Morocco and Algeria
Riwaya Types
export type Riwaya = 'hafs' | 'warsh' | undefined ;
export type RiwayaArabic = 'حفص' | 'ورش' ;
How to Switch Riwayas
First Launch
On first launch, you’ll be prompted to select your preferred Riwaya
Choose Riwaya
Select either حفص (Hafs) or ورش (Warsh) from the segmented control
Change Later
You can change your Riwaya anytime from Settings using the same segmented control
Selection Component
components/SelectRiwaya.tsx
< SegmentedControl
options = { riwayaOptions } // ['حفص', 'ورش']
initialSelectedIndex = { RiwayaByIndice ( mushafRiwayaValue ) }
activeColor = { primaryColor }
textColor = { primaryColor }
onSelectionChange = { ( index : number ) => {
const selectedRiwaya = RiwayaByValue ( index );
setMushafRiwayaValue ( selectedRiwaya );
} }
/>
Technical Implementation
State Management
Riwaya selection is stored using Jotai atoms with persistent storage:
export const mushafRiwaya = createAtomWithStorage < Riwaya | undefined >(
'MushafRiwaya' ,
undefined ,
);
The Riwaya preference is saved to device storage and persists across app sessions.
Image Map Selection
The app maintains separate image collections for each Riwaya:
let imagesMap ;
switch ( mushafRiwayaValue ) {
case 'hafs' :
imagesMap = imagesMapHafs ;
break ;
case 'warsh' :
imagesMap = imagesMapWarsh ;
break ;
default :
imagesMap = undefined ;
}
Each Riwaya has its own Quran metadata (Surah info, Hizb divisions, etc.):
hooks/useQuranMetadata.ts
if ( mushafRiwayaValue === 'hafs' ) {
// Load Hafs metadata
thumnDataJSON = await import ( '@/assets/metadata/hafs/thumun.json' );
surahDataJSON = await import ( '@/assets/metadata/hafs/suwar.json' );
// ... more Hafs files
} else {
// Load Warsh metadata (default)
thumnDataJSON = await import ( '@/assets/metadata/warsh/thumun.json' );
surahDataJSON = await import ( '@/assets/metadata/warsh/suwar.json' );
// ... more Warsh files
}
Differences Between Riwayas
Layout Variations
The number of pages may differ between Hafs and Warsh Mushafs, as they use different layouts and line breaks.
Some words are recited differently between Riwayas, reflected in the Mushaf text (e.g., مَالِكِ vs مَلِكِ).
Both Mushafs include color-coded Tajweed rules, but the specific marks may vary based on recitation rules.
Automatic Updates
When you switch Riwayas, the app automatically:
Reloads Metadata
Loads the appropriate Surah, Hizb, and page data for the selected Riwaya
Switches Images
Updates the page images to use the correct Mushaf layout
Clears Cache
Removes cached pages from the previous Riwaya to free up storage
Maintains Position
Attempts to keep you at a similar location in the Quran when switching
Changing Riwayas requires reloading assets, so there may be a brief loading period when switching.
Utility Functions
The app provides helper functions for working with Riwayas:
// Get the index of a Riwaya value (for UI components)
export function RiwayaByIndice ( value : Riwaya ) : number {
return riwayaArray . indexOf ( value );
}
// Get the Riwaya value by index (from UI selection)
export function RiwayaByValue ( index : number ) : Riwaya {
return riwayaArray [ index ];
}
The Android widget also respects your Riwaya selection:
widgets/widget-task-handler.tsx
const riwaya = store . get ( mushafRiwaya ) || 'warsh' ;
// Widget loads appropriate images based on Riwaya
If no Riwaya is selected, the app defaults to Warsh for backward compatibility.