Home  /  Blog  /  Quran Word by Word App

Build a Word-by-Word Quran Learning App

Tutorial8 min readSeptember 2026

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:

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:

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