Build a Word-by-Word Quran Learning App
This post shows you how to build a quran word by word app that displays each Arabic word alongside its transliteration and English meaning. We use the /api/quran/words endpoint from UmmahAPI to get all the data we need.
What We Are Building
The app lets a user pick a surah and an ayah. It then fetches every word in that ayah and displays them in a card-style layout. Each card shows the Arabic word, how to pronounce it in English letters, and what it means. This is a common pattern for Arabic learners studying the Quran.
Here is what the final result does:
- User selects a surah number and ayah number
- App fetches the word-by-word breakdown from the API
- Each word appears in its own card with Arabic, transliteration, and meaning
- Clicking a card highlights it so the user can study one word at a time
We will write this in plain HTML, CSS, and JavaScript. No build tools needed. You can drop this into any project or framework later.
The Word-by-Word Endpoint
The endpoint is simple. You pass a surah number and an ayah number, and you get back an array of word objects.
// Fetch word-by-word data for Surah Al-Fatiha, Ayah 1
// GET https://ummahapi.com/api/quran/words/1/1
const response = await fetch('https://ummahapi.com/api/quran/words/1/1');
const data = await response.json();
console.log(data);
A single word object in the response looks something like this:
{
"position": 1,
"arabic": "بِسْمِ",
"transliteration": "Bismi",
"translation": "In (the) name"
}
The position field tells you the word's order in the ayah. The arabic field has the Arabic text. The transliteration field has the pronunciation in Latin letters. The translation field has the English meaning.
Tip. You can add ?script=uthmani or ?script=indopak to the request URL if you want a specific Arabic script style. For example: /api/quran/words/1/1?script=uthmani.
Project Setup
Create a folder and add three files. That is all the setup you need.
quran-word-app/
index.html
style.css
app.js
Start with the HTML. This gives us a surah selector, an ayah input, a button, and a container where the word cards will appear.
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Quran Word by Word</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>Quran Word by Word</h1>
<div class="controls">
<input type="number" id="surah" placeholder="Surah (1-114)"
min="1" max="114" value="1">
<input type="number" id="ayah" placeholder="Ayah"
min="1" value="1">
<button id="loadBtn">Load Ayah</button>
</div>
<div id="status" class="status"></div>
<div id="words" class="words-grid"></div>
</div>
<script src="app.js"></script>
</body>
</html>
Fetching and Rendering the Words
Now write the JavaScript. The loadWords function reads the surah and ayah values, calls the API, and builds a card for each word.
// app.js
const BASE = 'https://ummahapi.com/api';
async function loadWords() {
const surah = document.getElementById('surah').value;
const ayah = document.getElementById('ayah').value;
const wordsEl = document.getElementById('words');
const statusEl = document.getElementById('status');
if (!surah || !ayah) {
statusEl.textContent = 'Please enter a surah and ayah number.';
return;
}
statusEl.textContent = 'Loading...';
wordsEl.innerHTML = '';
try {
const res = await fetch(`${BASE}/quran/words/${surah}/${ayah}`);
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
const data = await res.json();
statusEl.textContent = '';
// data.words is the array of word objects
const words = data.words || data;
words.forEach(word => {
const card = document.createElement('div');
card.className = 'word-card';
card.innerHTML = `
<div class="arabic" dir="rtl">${word.arabic}</div>
<div class="translit">${word.transliteration}</div>
<div class="meaning">${word.translation}</div>
`;
card.addEventListener('click', () => {
document.querySelectorAll('.word-card')
.forEach(c => c.classList.remove('active'));
card.classList.add('active');
});
wordsEl.appendChild(card);
});
} catch (err) {
statusEl.textContent = `Error: ${err.message}. Check the surah and ayah numbers.`;
}
}
document.getElementById('loadBtn')
.addEventListener('click', loadWords);
// Load a default ayah on page open
loadWords();
Styling the Word Cards
The CSS handles the grid layout and the Arabic font. Arabic text needs direction: rtl so it reads right to left. We use the Amiri font from Google Fonts because it renders Quranic script clearly.
/* style.css */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=Amiri:wght@400;700&display=swap');
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Inter', sans-serif;
background: #f5f7fa;
color: #1a1a1a;
padding: 2rem 1rem;
}
.container { max-width: 900px; margin: 0 auto; }
h1 {
font-size: 1.6rem;
margin-bottom: 1.5rem;
color: #1a7a4a;
}
.controls {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
margin-bottom: 1.5rem;
}
.controls input {
padding: 0.5rem 0.75rem;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 0.95rem;
width: 140px;
}
.controls button {
padding: 0.5rem 1.2rem;
background: #1a7a4a;
color: #fff;
border: none;
border-radius: 6px;
font-size: 0.95rem;
cursor: pointer;
font-weight: 600;
}
.controls button:hover { background: #155f3a; }
.status { color: #666; font-size: 0.9rem; margin-bottom: 1rem; min-height: 1.2em; }
.words-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: 1rem;
}
.word-card {
background: #fff;
border: 2px solid #e2e8f0;
border-radius: 10px;
padding: 1rem 0.75rem;
text-align: center;
cursor: pointer;
transition: border-color 0.15s, box-shadow 0.15s;
}
.word-card:hover {
border-color: #1a7a4a;
box-shadow: 0 2px 8px rgba(26,122,74,0.12);
}
.word-card.active {
border-color: #1a7a4a;
background: #e8f5ee;
}
.arabic {
font-family: 'Amiri', serif;
font-size: 1.6rem;
color: #1a7a4a;
direction: rtl;
margin-bottom: 0.5rem;
line-height: 1.8;
}
.translit {
font-size: 0.82rem;
font-style: italic;
color: #555;
margin-bottom: 0.3rem;
}
.meaning {
font-size: 0.82rem;
font-weight: 600;
color: #1a1a1a;
}
Open index.html in a browser. The first ayah of Surah Al-Fatiha loads right away. Each word appears as a card. Click any card to highlight it.
Adding a Surah List for Better Navigation
Typing surah numbers is fine, but a dropdown with surah names is nicer. Use the /api/quran/surahs endpoint to populate a select element when the app loads.
// Add this to app.js
async function populateSurahs() {
const res = await fetch('https://ummahapi.com/api/quran/surahs');
const data = await res.json();
// Replace the number input with a select element
const input = document.getElementById('surah');
const select = document.createElement('select');
select.id = 'surah';
data.surahs.forEach(s => {
const opt = document.createElement('option');
opt.value = s.number;
opt.textContent = `${s.number}. ${s.englishName}`;
select.appendChild(opt);
});
input.parentNode.replaceChild(select, input);
}
populateSurahs();
Now the user can pick a surah by name instead of memorizing numbers. The response from /api/quran/surahs includes the surah number, English name, Arabic name, and the number of ayahs. You can use the ayah count to validate the ayah input and prevent out-of-range requests.
Tip. Store the surah data in a variable after the first fetch. Then you can check how many ayahs a surah has without making another API call. This saves requests and keeps the app fast.
Loading a Random Ayah
A "Random Ayah" button is a good way to help users discover new words. The /api/quran/random endpoint returns a random ayah including its surah and ayah numbers. You can use those numbers to call the word-by-word endpoint right after.
// Add this to app.js
async function loadRandom() {
const statusEl = document.getElementById('status');
statusEl.textContent = 'Getting a random ayah...';
const res = await fetch('https://ummahapi.com/api/quran/random');
const data = await res.json();
// data.ayah has surahNumber and ayahNumber
const { surahNumber, ayahNumber } = data.ayah;
// Update the inputs so the user can see which ayah was loaded
document.getElementById('surah').value = surahNumber;
document.getElementById('ayah').value = ayahNumber;
loadWords();
}
// Add a random button in your HTML and wire it up
document.getElementById('randomBtn')
.addEventListener('click', loadRandom);
Add a button with id="randomBtn" to your HTML controls section. That is all the wiring you need.
Using the API in React
If you are building with React, the same fetch logic works inside a useEffect. Here is a minimal component that renders word cards.
// WordByWord.jsx
import { useState, useEffect } from 'react';
const BASE = 'https://ummahapi.com/api';
export default function WordByWord({ surah = 1, ayah = 1 }) {
const [words, setWords] = useState([]);
const [active, setActive] = useState(null);
const [error, setError] = useState('');
useEffect(() => {
async function load() {
try {
const res = await fetch(`${BASE}/quran/words/${surah}/${ayah}`);
const data = await res.json();
setWords(data.words || data);
} catch (e) {
setError(e.message);
}
}
load();
}, [surah, ayah]);
if (error) return <p>Error: {error}</p>;
return (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '1rem' }}>
{words.map((word, i) => (
<div
key={i}
onClick={() => setActive(i)}
style={{
border: active === i ? '2px solid #1a7a4a' : '2px solid #e2e8f0',
borderRadius: '10px',
padding: '1rem',
textAlign: 'center',
cursor: 'pointer',
minWidth: '120px'
}}
>
<div dir="rtl" style={{ fontFamily: 'Amiri, serif', fontSize: '1.5rem' }}>
{word.arabic}
</div>
<div style={{ fontSize: '0.8rem', fontStyle: 'italic', color: '#555' }}>
{word.transliteration}
</div>
<div style={{ fontSize: '0.8rem', fontWeight: '600' }}>
{word.translation}
</div>
</div>
))}
</div>
);
}
Pass surah and ayah as props. The component re-fetches whenever those props change, so you can wire it up to any navigation you already have.
Handling Rate Limits
Without an API key, the limit is 5,000 requests per 15 minutes. For most learning apps that is plenty. If you expect heavier traffic, or if you are building something for a community, grab a free key at ummahapi.com/register. A free key removes the rate limit.
Pass the key as a header in your fetch calls:
const res = await fetch(
'https://ummahapi.com/api/quran/words/2/255',
{
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
}
);
Keep your key out of frontend code if the app is public. Use an environment variable and a small backend route to proxy the request instead. In Next.js that means a route in app/api/. In Express it means a simple app.get that calls UmmahAPI and forwards the response.
What to Build Next
Once the core word-by-word view is working, there are a few natural extensions:
- Add audio playback. Use the
?reciter=alafasyquery param on the single ayah endpoint to get an audio URL, then play it with the HTMLAudioAPI. - Show a full translation below the cards. Call
/api/quran/surah/:s/ayah/:a?translations=enand display the full ayah text as context. - Add a quiz mode. Hide the meaning cards and ask the user to type the meaning before revealing it.
- Save progress in
localStorage. Track which words the user has studied so they can pick up where they left off.
Check the full API docs for all the query parameters available on each endpoint. The Quran Memorization Tracker tutorial also shows how to handle localStorage for progress tracking.
FAQ
What does the UmmahAPI word-by-word endpoint return?
The endpoint at /api/quran/words/:surah/:ayah returns an array of word objects for that ayah. Each object includes the Arabic text, transliteration, and English meaning for that individual word.
Do I need an API key to use the word-by-word endpoint?
No API key is required to get started. The free tier allows 5,000 requests per 15 minutes. Registering for a free key at ummahapi.com/register removes that rate limit entirely.
Can I build a quran word by word app with plain JavaScript?
Yes. The API returns JSON so you can use it with plain fetch() in the browser. No special library or SDK is needed. The examples in this post all use plain JavaScript.
How do I display Arabic text correctly in a web app?
Set the dir attribute to rtl on the element containing Arabic text. Use a font that supports Arabic script, such as Amiri from Google Fonts. UmmahAPI also supports the Uthmani and Indopak scripts via the ?script query parameter.
Start Building for Free
UmmahAPI gives you the Quran, Hadith, Prayer Times, and more in one free API.
Read the docs