Ambidex
Effortlessly host your React app on both the client and the server. Some call it isomorphic JavaScript - we call it Ambidex.
Install / Use
/learn @appsforartists/AmbidexREADME
Introduction
Ambidex lets you render the exact same React.js app on both the client and the server. It abstracts away the differences between them, so you can focus your creativity where it matters most: on your product.

Ambidex is being incubated in the eBay Mobile Innovations lab.
Presentations
-
The Ultimate Workflow: Tweak your page in real time without leaving the comfort of your editor
Presented at React.js Conf 2015
Ingredients
Ambidex brings together the best of a bunch other fantastic projects, including:
- React by Facebook
- React Router by Ryan Florence and Michael Jackson
- React Hot Loader by Dan Abramov
- Webpack by Tobias Koppers
- Mach by Michael Jackson
Requirements
Ambidex has been tested on
iojs v1.0.1npm v2.2.0
It should also work on node v0.11.13 with the --harmony flag, but no promises.
Stability
Ambidex is a work-in-progress. It is the foundation of our web-centric work in the eBay Mobile Innovation lab, but it has not yet been deployed in production.
It is being developed in the open so we, as a community, can share ideas and best practices around being insanely productive with React. When you see something you'd like to improve, please start an issue or send us a message.
How it works
Ambidex renders the initial request on the server and subsequent requests in the browser. Rendering on the server is beneficial for SEO, time-to-glass, and supporting clients that don't speak JavaScript (like robots). Rendering on the client saves bandwidth, reduces server load, and makes your app feel more responsive (because changes happen instantly). Ambidex brings you the benefits of both.
It is designed to be used in conjunction with a Service-Oriented Architecture. This means your data layer should be provided as a RESTful API that functions independently of your web app.
This architecture has two advantages:
-
Your data platform is capable of supporting any client, including native mobile apps or those of third parties.
-
Your app can access data directly from the browser, so it doesn't waste server cycles requesting largely-redundant HTML on every page load.
Getting Started
(Dive right in by perusing our sample application.)
Here's how you instantiate Ambidex:
new Ambidex(
{
"settings": require(`./settings.${ process.env["NODE_ENV"] }.js`),
"middlewareInjector": function (stack) {
// If you want to edit your Mach stack (for instance,
// to serve static files with mach.file) do that here.
}
}
).then(
(ambidex) => {
}
);
Notice that the Ambidex constructor returns a promise. This promise will be resolved when your app is serving as expected.
Each Ambidex instance has three public properties:
ambidex.stack: the Mach stack that is serving your app,ambidex.webpack: the Webpack instance that bundles your files, andambidex.webpackDevServer: the Webpack Dev Server instance that serves your files whenENABLE_HOT_MODULE_REPLACEMENTistrue.
Settings
Each instance of Ambidex starts with a settings dictionary. Here are the individual settings supported:
settings["NAME"]
The name your app will be referred to in the logs, "My Awesome App".
settings["SHORT_NAME"] (optional)
A variation of that name without spaces or capital letters - your app will appear in process monitors like top under this name, "my_awesome_app"
settings["HOST"]
The hostname that your app will be made available at, "example.appspot.com".
settings["PORT"]
The port that goes in your browser's address bar, "80".
settings["VM_PORT"] (optional)
If your app is behind a load balancer, this is the port that Ambidex should serve your app so the load balancer can find it, "8080".
settings["BASE_URL"] (optional)
If your app is behind displayed inside a TardisGallery, mount its route tree here. This allows you to serve different variations of your app alongside one another; for instance, you might serve version A at "/A/" and version B at /B/.
settings["ENABLE_HOT_MODULE_REPLACEMENT"] (optional)
If this is true, you'll be able to edit your app live with react-hot-loader. This should be true for whatever environment(s) you're developing on.
settings["TITLE_SEPARATOR"] (optional)
This string gets put in between section titles, such as " - ".
settings["FAV_ICON_URL"]
For instance, "/static/logo.svg".
settings.FILESYSTEM_PATHS
In order to make your app Ambidextrous, we need to know where certain files are kept. The values in this dictionary answer that question.
settings.FILESYSTEM_PATHS["BASE"]
All the other FILESYSTEM_PATHS are relative to this one. If you set this to __dirname (no quotes), they will be relative to your settings file.
settings.FILESYSTEM_PATHS["ROUTES"]
The module at this path should export the root <Route> of your route tree.
settings.FILESYSTEM_PATHS["STYLES"] (optional)
By default, Ambidex will set the same defaults as React Native, such as using flexbox for all block-level elements. It also defaults to Roboto, the Material Design font used across Android.
If you'd like to override those defaults, simply point FILESYSTEM_PATHS["STYLES"] your own CSS file.
settings.FILESYSTEM_PATHS["FUNX_DEFINITIONS"] (optional)
This module should export a dictionary of Funx definitions, e.g.:
module.exports = {
"apiDefinitions": {
"Bikes": require("./apis/Bikes.js"),
},
"storeDefinitions": {
"stateful": {
"Bikes": require("./stores/Bikes.js"),
},
"ephemeral": {
"currentBike": function (Bikes, routerState) {
return routerState.has("bikeID")
? Bikes.getOrFetch(
{
"bikeID": routerState.get("bikeID")
}
)
: null
},
"readyToRender": function (routerState, currentBike) {
if (routerState.has("bikeID") && !currentBike)
return false;
return true;
},
},
}
}
Until I have time to document this better, see Gravel's funxDefinitions for a very simple example.
settings.FILESYSTEM_PATHS["BUNDLES"]
When "ENABLE_HOT_MODULE_REPLACEMENT" is false (e.g. in production), Ambidex will run Webpack on all your files and serve the results inline in every response. To do so, it need to be able to cache them on the filesystem.
"BUNDLES" should resolve to a folder where it can store these files. Each instance of Ambidex needs its own unique bundles
Related Skills
node-connect
346.8kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
346.8kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
346.8kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
