If a daily-content project starts as a folder of JSON files, the first useful web interface does not need a database server. It needs a predictable data shape, a small client-side state model, and a deployment target that can serve static files.
This tutorial builds that path with AA Daily Reflections Database, an open-source React portal that publishes English, Spanish, French, and Brazilian Portuguese data as JSON, CSV, and SQLite. The goal is not to reproduce its reflection text here. The goal is to understand the reusable engineering pattern: select a language, select a date, load one static data file, and deploy the result as a static site.
TL;DR
Clone the repository, install its Node.js dependencies, run the data-copy step, and build the React app. The portal keeps the selected language and date in localStorage, fetches the matching language JSON from public/data, and uses PUBLIC_URL so the same build can work locally or beneath a GitHub Pages project path.
The repository currently targets React 18.2.0 and react-scripts 5.0.1 in package.json. The live site is available on GitHub Pages.
Prerequisites
You need:
- Node.js and npm
- Git
- A browser
- A GitHub repository if you plan to publish your own fork
The project also includes Python, PHP, Go, and JavaScript examples for reading the SQLite database. This walkthrough focuses on the React portal and its static JSON boundary. See the repository's examples guide for the other language-specific paths.
1. Get the project and inspect the data boundary
Clone the repository and enter it:
git clone https://github.com/paladini/aa-daily-reflections-database.git cd aa-daily-reflections-database The repository stores the browser-facing files in public/data. Each JSON record has a date, language, title, quote, reflection text, and source reference. The data README documents the same fields and also shows the SQLite schema.
That separation matters. The React app does not need a server endpoint for every date. It can fetch one language file, keep it in memory, and find the requested date locally. Static hosting is enough for this read-only workflow.
2. Install dependencies and build the portal
Install from the lockfile, then run the production build:
npm ci npm run build The prebuild script runs npm run copy-data first. That script copies the four language files into public/data, which is the location the browser service reads at runtime. The build then emits a static build directory.
If you want to inspect the development version before building, run:
npm start Open the local address shown by Create React App. The application uses the current date as its initial selection and starts in Brazilian Portuguese. You can change both values from the interface.
3. See how language selection becomes a file request
The central mapping lives in src/context/AppContext.js. It maps an application language key to a concrete public filename:
export const LANGUAGES = { 'pt-br': { code: 'pt-br', name: 'Portuguese (Brazil)', file: 'daily_reflections_brazilian-portuguese.json', }, en: { code: 'en', name: 'English', file: 'daily_reflections_english.json', }, }; The current source contains all four languages. The important design choice is that the UI does not construct arbitrary paths from user input. It selects a file from a fixed allowlist. That keeps the fetch boundary easy to audit.
The same context stores language and selectedDate, then persists them under two local-storage keys. A visitor can return later and keep the last selected language and date on that browser. This is convenience state, not account data, and it never needs a server session.
4. Load one language file and find one date
The data service in src/services/reflectionService.js caches each loaded language file. Its core path is equivalent to this:
const dataCache = {}; const loadLanguageData = async (lang) => { if (dataCache[lang]) return dataCache[lang]; const languageInfo = LANGUAGES[lang]; const baseUrl = process.env.PUBLIC_URL || ''; const response = await fetch( `${baseUrl}/data/${languageInfo.file}`, ); if (!response.ok) { throw new Error(`Could not load data: ${response.status}`); } const data = await response.json(); dataCache[lang] = data; return data; }; export const getReflection = async (lang, date) => { const data = await loadLanguageData(lang); return data.find((item) => item.date === date) || null; }; The PUBLIC_URL prefix is important on GitHub Pages. A project site is served below a path such as /aa-daily-reflections-database, not necessarily from /. The prefix lets Create React App provide the correct base path during the build while keeping local development simple.
5. Verify the result without trusting the screen alone
Use the build output and the browser network panel as two separate checks.
First, confirm that the build created the copied files:
test -f build/data/daily_reflections_english.json test -f build/data/daily_reflections_brazilian-portuguese.json On PowerShell, use:
Test-Path build/data/daily_reflections_english.json Test-Path build/data/daily_reflections_brazilian-portuguese.json Then open the app and change the language. In the browser's Network panel, verify that the request changes to the corresponding file under data/. Change the date and confirm that the displayed record changes without a server-side API request.
Finally, refresh the page. The selected language and date should be restored because the context writes them to localStorage. If a JSON request returns 404, inspect the base path first. A missing PUBLIC_URL is a common static-hosting failure mode.
6. Publish the static build
The repository already includes a deploy script using the gh-pages package:
npm run deploy Before using it, review the generated build directory and confirm that the repository's Pages configuration matches the branch and path you intend to publish. GitHub Pages is a static host here. It does not execute the React code on the server and it cannot provide private storage for reflection data or user preferences.
The project also generates a sitemap as part of its deployment-oriented script. Read scripts/generate-sitemap.js before adapting the domain or path for a fork. SEO metadata is only useful when its canonical URLs match the site that is actually being served.
Why this pattern works
This architecture matches the problem's access pattern:
- The data is read-heavy and changes infrequently.
- Each language can be downloaded independently.
- Date selection is a local lookup over an already loaded array.
- The UI needs no authentication or mutation endpoint.
- The deployment artifact is ordinary HTML, JavaScript, CSS, and JSON.
The tradeoff is equally clear. Every browser that uses a language downloads that language's JSON file, and searching the full collection happens in the browser. If the corpus grows substantially, replace the array lookup with a generated index or a query service. Do not add a database server merely because the source data is also available as SQLite.
Limitations and security boundaries
The repository's MIT license covers the software compilation and examples, but its legal notice separately says that the original Alcoholics Anonymous materials remain copyrighted and that the compilation is unofficial. Do not treat the MIT notice as permission to republish the underlying literature without checking the rights for your use case. The project links to aa.org for official resources.
The portal also is not a medical service, a replacement for a recovery group, or a guarantee that a translation is official. Keep those boundaries visible if you fork it. Do not put private user notes into this static dataset: GitHub Pages publishes the generated files publicly.
FAQ
Do I need a backend?
Not for the included read-only portal. The browser fetches static JSON and performs the date lookup locally.
Why keep JSON, CSV, and SQLite?
They serve different consumers. JSON is convenient for browser and small scripts, CSV is useful for analysis, and SQLite is useful when a client needs indexed queries or joins.
Can I add another language?
Add a validated data file, register it in the language allowlist, copy it into public/data, and update the UI labels and documentation. Verify dates, encoding, attribution, and rights before publishing.
Takeaway
A multilingual daily-content portal can remain simple when the data contract is explicit. Start with a fixed language allowlist, cache one static file per language, persist only non-sensitive preferences, and make the deployment path part of the verification checklist.
What would you add first to this pattern: a generated search index, an accessibility test suite, or a small API for clients that cannot download the full language file?
AI assistance disclosure: AI assistance was used to organize and edit this tutorial. The repository links, commands, architecture notes, limitations, and security boundaries were checked against the current public repository and live site before publication.

