diff --git a/modules/helpers/resolveAsyncState.js b/modules/helpers/resolveAsyncState.js
new file mode 100644
index 0000000000..5faee7f1d4
--- /dev/null
+++ b/modules/helpers/resolveAsyncState.js
@@ -0,0 +1,25 @@
+var Promise = require('es6-promise').Promise;
+
+/**
+ * Resolves all values in asyncState and calls the setState
+ * function with new state as they resolve. Returns a promise
+ * that resolves after all values are resolved.
+ */
+function resolveAsyncState(asyncState, setState) {
+ if (asyncState == null)
+ return Promise.resolve();
+
+ var keys = Object.keys(asyncState);
+
+ return Promise.all(
+ keys.map(function (key) {
+ return Promise.resolve(asyncState[key]).then(function (value) {
+ var newState = {};
+ newState[key] = value;
+ setState(newState);
+ });
+ })
+ );
+}
+
+module.exports = resolveAsyncState;
diff --git a/modules/main.js b/modules/main.js
index c840e54685..e5421c7371 100644
--- a/modules/main.js
+++ b/modules/main.js
@@ -7,6 +7,7 @@ exports.replaceWith = require('./helpers/replaceWith');
exports.transitionTo = require('./helpers/transitionTo');
exports.ActiveState = require('./mixins/ActiveState');
+exports.AsyncState = require('./mixins/AsyncState');
// Backwards compat with 0.1. We should
// remove this when we ship 1.0.
diff --git a/modules/mixins/AsyncState.js b/modules/mixins/AsyncState.js
new file mode 100644
index 0000000000..6206b50986
--- /dev/null
+++ b/modules/mixins/AsyncState.js
@@ -0,0 +1,113 @@
+var React = require('react');
+var resolveAsyncState = require('../helpers/resolveAsyncState');
+
+/**
+ * A mixin for route handler component classes that fetch at least
+ * part of their state asynchronously. Classes that use it should
+ * declare a static `getInitialAsyncState` method that fetches state
+ * for a component after it mounts. This function is given three
+ * arguments: 1) the current route params, 2) the current query and
+ * 3) a function that can be used to set state as it is received.
+ *
+ * Much like the familiar getInitialState method, getInitialAsyncState
+ * should return a hash of key/value pairs to use in the component's
+ * state. The difference is that the values may be promises. As these
+ * values resolve, the component's state is updated. You should only
+ * ever need to use the setState function for doing things like
+ * streaming data and/or updating progress.
+ *
+ * Example:
+ *
+ * var User = React.createClass({
+ *
+ * statics: {
+ *
+ * getInitialAsyncState: function (params, query, setState) {
+ * // If you don't need to do anything async, just update
+ * // the state immediately and you're done.
+ * setState({
+ * user: UserStore.getUserByID(params.userID)
+ * });
+ *
+ * // Or, ignore the setState argument entirely and return a
+ * // hash with keys named after the state variables you want
+ * // to set. The values may be immediate values or promises.
+ * return {
+ * user: getUserByID(params.userID) // may be a promise
+ * };
+ *
+ * // Or, stream your data!
+ * var buffer = '';
+ *
+ * return {
+ *
+ * // Same as above, the stream state variable is set to the
+ * // value returned by this promise when it resolves.
+ * stream: getStreamingData(params.userID, function (chunk) {
+ * buffer += chunk;
+ *
+ * // Notify of progress.
+ * setState({
+ * streamBuffer: buffer
+ * });
+ * })
+ *
+ * };
+ * }
+ *
+ * },
+ *
+ * getInitialState: function () {
+ * return {
+ * user: null, // Receives a value when getUserByID resolves.
+ * stream: null, // Receives a value when getStreamingData resolves.
+ * streamBuffer: '' // Used to track data as it loads.
+ * };
+ * },
+ *
+ * render: function () {
+ * if (!this.state.user)
+ * return
Welcome {this.state.user.name}!
+ *So far, you've received {this.state.streamBuffer.length} data!
+ *