madeenan

React Integration Guide

Add Quran and Hadith Search to a React App

A React integration that keeps secrets off the client and treats source retrieval as a structured product flow.

What You Will Build

A React search screen that calls your own backend, groups Quran and Hadith results, and never exposes the Madeenan API key.

Prerequisites

  • React 18 or newer
  • A backend proxy endpoint
  • A Madeenan API key stored only on the backend

Call Your Backend, Not Madeenan Directly

A search box makes an API call feel like frontend work. The credential boundary says otherwise. Anything shipped to a browser can be inspected, so the React client should send the query to an endpoint your team controls. That endpoint authenticates with Madeenan, applies your product rules, and returns only the source fields the interface needs.

The proxy is also the right place to enforce query length, authenticated-user limits, allowed source families, and request timeouts. Keeping those decisions on the server gives you one policy surface even if the product later adds a mobile client or an embed.

Return a small application-level response instead of forwarding every upstream field. Preserve stable source IDs, types, references, Arabic, translations, grades, and display labels. Drop internal diagnostics unless the current user is supposed to see them.

export async function searchIslamicSources(query: string) {
const response = await fetch("/api/islamic-search", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query })
});
if (!response.ok) throw new Error("source_search_failed");
return response.json();
}

Model Loading, Empty, and Error States

Search quality is only half the experience. A user also needs to know whether the interface is waiting for input, retrieving sources, returning no confident matches, or recovering from a failed request. One blank results panel cannot communicate all four states.

Model these states as a discriminated union so the component cannot accidentally render results and an error at the same time. The type also forces future contributors to handle a new state deliberately instead of adding another loosely related boolean.

When the query changes quickly, cancel the previous request with an AbortController or ignore its response using a request identifier. Without that guard, a slow response for an older query can replace the results for the phrase currently visible in the input.

type SearchState =
| { status: "idle" }
| { status: "loading" }
| { status: "ready"; results: Record<string, Source[]> }
| { status: "empty" }
| { status: "error"; message: string };

Group Results by Source Family

Do not flatten Quran, Hadith, and Tafsir into one visually identical relevance list. The source family changes how a reader understands the result. A clear heading and source-specific metadata make that distinction visible before the user reads the text.

Within each group, keep the reference close to the excerpt. Quran cards should expose surah and ayah. Hadith cards should show collection, report number, and grade when the payload provides them. Translation and Arabic need separate typographic treatment, not one paragraph with mixed directionality.

If one source family has no matches, omit that empty group rather than presenting a warning. Reserve the global empty state for the case where the retrieval response contains no useful results at all.

Security Boundary

A React environment variable is not secret when bundled for the browser. The Madeenan key belongs in a backend function or server process.

Test Before Shipping

  • Inspect the browser bundle for the key
  • Test idle, loading, empty, and error states
  • Keep Quran and Hadith headings visible
  • Cancel stale requests when the query changes