Why Expo and React Native for WordPress
When it comes to building a cross-platform mobile app that connects to WordPress, Expo and React Native offer a combination of advantages that no other framework matches in 2026. You write one codebase in TypeScript that runs natively on both iOS and Android. You get over-the-air updates that bypass App Store review cycles for content and UI changes. You have access to the largest ecosystem of mobile development libraries in the JavaScript world. And with Expo's managed workflow, you can build and deploy without touching Xcode or Android Studio.
React Native is not a WebView. This distinction is critical. When you use React Native, your JavaScript code drives actual native UI components. A Text component in React Native becomes a UILabel on iOS and a TextView on Android. An Image component uses the platform's native image loading pipeline. A ScrollView uses the platform's native scroll physics. The JavaScript runs in a separate thread and communicates with the native side through a bridge — but the UI itself is entirely native.
Expo adds a layer of convenience on top of React Native. It handles the native build configuration, provides a curated set of native modules (camera, file system, notifications, etc.), and offers Expo Go for rapid development without compiling native code. For a WordPress content app, Expo's managed workflow is more than sufficient — you never need to eject or write native Swift or Kotlin code.
Step 1: Connecting to the WordPress REST API
Every WordPress installation since version 4.7 includes a built-in REST API. This API exposes your posts, pages, categories, tags, media, and users through standard HTTP endpoints. The base URL follows this pattern:
https://yoursite.com/wp-json/wp/v2/posts
This endpoint returns a JSON array of your most recent posts, including the title, content (as HTML), excerpt, featured image ID, author ID, category IDs, and publication date. You can filter by category, search by keyword, and paginate through results using query parameters.
For a basic implementation, you need three endpoints: /wp/v2/posts for the post list and individual posts, /wp/v2/categories for the category list, and /wp/v2/media for featured image URLs. Authentication is not required for reading public content, which simplifies the initial setup considerably.
In your Expo app, you would create a service module that encapsulates all API calls. Using TypeScript, you define interfaces for the WordPress response types and create typed fetch functions. This gives you autocomplete and type checking throughout your app, catching data-shape issues at compile time rather than at runtime.
A common pattern is to define a configuration constant for the WordPress base URL — the one value that buyers or implementers need to change to point the app at their own site. Everything else in the API layer derives from this single constant.
Step 2: The Content Rendering Problem
Here is where most WordPress mobile app projects hit their first major obstacle. The WordPress REST API returns content as HTML. Not simple HTML — Gutenberg block HTML, complete with nested divs, CSS classes, figure elements, block comments, and inline styles. A typical post response looks something like this:
<!-- wp:paragraph --><p>Your content here...</p><!-- /wp:paragraph --><!-- wp:image {"id":123} --><figure class="wp-block-image">...</figure><!-- /wp:image -->
React Native cannot render HTML. There is no DOM, no CSS engine, no browser rendering pipeline. You have three options for displaying this content:
- WebView: Embed a web browser component and load the HTML directly. This works but defeats the entire purpose of using React Native — you get web performance inside a native shell.
- HTML-to-Native parser: Use a library like react-native-render-html to parse HTML and map elements to React Native components. This works for simple HTML but struggles with Gutenberg's complex block structure, nested elements, and inline styles.
- Server-side conversion to Markdown: Convert the HTML to Markdown on the server before it reaches the app. Markdown has a clean, flat structure that maps naturally to native components. This is the approach NativePress uses.
The third option is the most robust. Markdown is a simplified representation of structured content — headings, paragraphs, bold text, images, links, lists, blockquotes, and code blocks. Each Markdown element maps one-to-one with a native component. There is no ambiguity, no complex nesting, and no CSS to interpret.
Step 3: Server-Side Markdown Conversion
The NativePress WordPress plugin handles the conversion from Gutenberg HTML to clean Markdown. It uses the league/html-to-markdown PHP library, which is the most mature and well-tested HTML-to-Markdown converter in the PHP ecosystem.
The conversion process involves several steps:
- Strip Gutenberg block comments: The <!-- wp:paragraph --> and <!-- /wp:paragraph --> comments are removed. They are useful for Gutenberg's block editor but meaningless for content display.
- Handle figure elements: WordPress wraps images in <figure> elements with optional <figcaption> children. These are converted to Markdown image syntax with alt text.
- Preserve semantic structure: Headings (h1-h6), lists (ul, ol), blockquotes, and code blocks are all converted to their Markdown equivalents, preserving the document's semantic hierarchy.
- Clean inline formatting: Bold, italic, links, and inline code are converted to Markdown inline syntax.
The plugin registers a custom REST API endpoint (under the /nativepress/v1/ namespace) that returns posts with a content_markdown field alongside the standard WordPress fields. This means your app can fetch both the original HTML (for fallback) and the pre-converted Markdown from a single API call.
Step 4: Architecture Decisions
State Management: Zustand over Redux
For a WordPress content app, the state is relatively simple: a list of posts, a list of categories, the current page for pagination, possibly a selected category filter, and app settings like theme colors and site name. This does not warrant the ceremony of Redux — actions, reducers, action types, middleware, selectors.
Zustand provides the same global state management with a fraction of the boilerplate. A Zustand store is a single function that returns an object. You read state with a hook. You update state by calling methods on the store. There are no providers to wrap your component tree in, no dispatch functions, and no action creators. For a content app with straightforward data flows, Zustand is the pragmatic choice.
Navigation: Expo Router
Expo Router brings file-based routing to React Native, similar to Next.js for the web. You create files in the app directory, and each file becomes a route. A file at app/(tabs)/home.tsx becomes the home tab. A file at app/post/[id].tsx becomes the dynamic post detail screen, where [id] is a route parameter.
This convention-over-configuration approach reduces the amount of navigation boilerplate significantly. Deep linking works automatically — if someone shares a link to a specific post, the app opens directly to that post's detail screen. Tab navigation, stack navigation, and modal presentation are all handled declaratively through the file structure.
TypeScript Throughout
TypeScript is not optional for a production WordPress app. The WordPress REST API returns complex nested objects with optional fields, nullable values, and varying shapes depending on query parameters. Without TypeScript, you are writing runtime checks and hoping for the best. With TypeScript, the compiler catches shape mismatches, missing null checks, and incorrect property access before the code ever runs on a device.
Define interfaces for your core types: Post, Category, Author, AppSettings. Use these types in your API service, your Zustand store, and your components. When WordPress changes an API response shape (which happens with plugin updates), TypeScript will immediately flag every place in your code that needs to adapt.
Step 5: The MarkdownRenderer Component
The most important component in the entire app is the MarkdownRenderer. This is the component that takes the Markdown string from the API and renders it as native UI. NativePress uses react-native-markdown-display, a library that parses Markdown and maps each element to a React Native component.
Out of the box, react-native-markdown-display handles headings, paragraphs, bold, italic, links, images, lists, blockquotes, code blocks, and horizontal rules. But for a polished app, you need custom render rules:
- Images: The default image component does not handle responsive sizing or caching. A custom rule wraps images in a component that calculates the correct aspect ratio, uses a cached image library, and handles loading states.
- Links: By default, links try to navigate within the app. A custom rule opens external links in the system browser (using expo-web-browser) while handling internal links through the app's navigation.
- Code blocks: The default code block is unstyled. A custom rule adds a background color, monospace font, horizontal scrolling for long lines, and optional syntax highlighting.
- Headings: Custom styling ensures headings use the proper font weight, size, and spacing to match the app's design system rather than the library's defaults.
The MarkdownRenderer component accepts the Markdown string as a prop and returns a fully rendered, scrollable, native view of the article. No HTML parsing. No CSS interpretation. No WebView. Just native components arranged according to the document structure.
The Complete Data Flow
Here is the full path from content creation to native display:
WordPress Gutenberg Editor (HTML) → NativePress Plugin (league/html-to-markdown) → REST API (content_markdown) → Expo App (fetch) → Zustand Store → MarkdownRenderer (react-native-markdown-display) → Native iOS / Android UI Components
At every step, the data is transformed into a simpler, more native-friendly format. By the time it reaches the user's screen, there is no HTML, no CSS, and no JavaScript in the rendering pipeline. The content is displayed through the same native components that Apple and Google use in their own first-party apps.
What You Get Out of the Box with NativePress
Building all of this from scratch is a substantial engineering effort. The REST API integration, the Markdown conversion, the custom render rules, the navigation structure, the state management, the TypeScript types, the dark mode support, the infinite scroll pagination, the category filtering — each piece is individually straightforward but together they represent weeks of development time.
The NativePress Developer Edition gives you all of this pre-built and production-ready. You get:
- The WordPress plugin with Markdown conversion and custom REST endpoints
- A complete Expo React Native app with TypeScript throughout
- Expo Router navigation with tabs and post detail screens
- Zustand state management with posts, categories, and settings
- A polished MarkdownRenderer with custom rules for images, links, and code
- Dark mode support driven by system preferences
- Infinite scroll pagination and category filtering
- A single configuration constant (WP_BASE_URL) to connect to any WordPress site
You own the code completely. There are no monthly fees, no vendor lock-in, and no restrictions on customization. Change the theme, add new screens, integrate additional APIs, or modify the Markdown rendering rules — it is your codebase.
Getting Started
If you want to build a WordPress mobile app with Expo and React Native, you have two paths:
Build from scratch: Follow the architecture outlined in this guide. Start with the WordPress REST API, solve the content rendering problem with server-side Markdown conversion, and build up the app layer by layer. Expect two to four weeks of development time for a production-ready result.
Start with NativePress: Install the WordPress plugin, grab the Developer Edition codebase, change the WP_BASE_URL constant, and you have a working app in minutes. Spend your time customizing the design and adding features instead of solving infrastructure problems that have already been solved.
Either way, the result is the same: a true native app that renders WordPress content beautifully on iOS and Android, without a WebView in sight.