diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 8a0f36e483..e93544799e 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -312,7 +312,10 @@ module.exports = React.createClass({
                 });
                 break;
             case 'view_room':
-                this._viewRoom(payload.room_id, payload.show_settings, payload.event_id, payload.invite_sign_url);
+                this._viewRoom(
+                    payload.room_id, payload.show_settings, payload.event_id,
+                    payload.invite_sign_url, payload.oob_data
+                );
                 break;
             case 'view_prev_room':
                 roomIndexDelta = -1;
@@ -350,6 +353,7 @@ module.exports = React.createClass({
                         room_id: foundRoom.roomId,
                         event_id: payload.event_id,
                         invite_sign_url: payload.invite_sign_url,
+                        oob_data: payload.oob_data,
                     });
                     return;
                 }
@@ -361,6 +365,7 @@ module.exports = React.createClass({
                         room_id: result.room_id,
                         event_id: payload.event_id,
                         invite_sign_url: payload.invite_sign_url,
+                        oob_data: payload.oob_data,
                     });
                 });
                 break;
@@ -448,7 +453,10 @@ module.exports = React.createClass({
     //
     // eventId is optional and will cause a switch to the context of that
     // particular event.
-    _viewRoom: function(roomId, showSettings, eventId, invite_sign_url) {
+    // @param {Object} oob_data Object of additional data about the room
+    //                               that has been passed out-of-band (eg.
+    //                               room name and avatar from an invite email)
+    _viewRoom: function(roomId, showSettings, eventId, invite_sign_url, oob_data) {
         // before we switch room, record the scroll state of the current room
         this._updateScrollMap();
 
@@ -461,6 +469,7 @@ module.exports = React.createClass({
             initialEventPixelOffset: undefined,
             page_type: this.PageTypes.RoomView,
             inviteSignUrl: invite_sign_url,
+            roomOobData: oob_data,
         };
 
         // if we aren't given an explicit event id, look for one in the
@@ -552,10 +561,12 @@ module.exports = React.createClass({
                     room_alias: self.starting_room_alias,
                     event_id: self.starting_event_id,
                     invite_sign_url: self.starting_room_invite_sign_url,
+                    oob_data: self.starting_room_oob_data,
                 });
                 delete self.starting_room_alias;
                 delete self.starting_event_id;
                 delete self.starting_room_invite_sign_url;
+                delete self.starting_room_oob_data;
             } else if (!self.state.page_type) {
                 if (!self.state.currentRoom) {
                     var firstRoom = null;
@@ -675,6 +686,13 @@ module.exports = React.createClass({
             var segments = screen.substring(5).split('/');
             var roomString = segments[0];
             var eventId = segments[1]; // undefined if no event id given
+
+            var oob_data = {
+                name: params.room_name,
+                avatarUrl: params.room_avatar_url,
+                inviterName: params.inviter_name,
+            };
+
             if (roomString[0] == '#') {
                 if (this.state.logged_in) {
                     dis.dispatch({
@@ -682,11 +700,16 @@ module.exports = React.createClass({
                         room_alias: roomString,
                         event_id: eventId,
                         invite_sign_url: params.signurl,
+                        oob_data: oob_data,
                     });
                 } else {
                     // Okay, we'll take you here soon...
+                    // XXX: There are way too many of these:
+                    // It would probably be better to handle whether the SDK is
+                    // ready or not in the view_room_alias handler instead.
                     this.starting_room_alias = roomString;
                     this.starting_room_invite_sign_url = params.signurl;
+                    this.starting_room_oob_data = oob_data;
                     this.starting_event_id = eventId;
                     // ...but you're still going to have to log in.
                     this.notifyNewScreen('login');
@@ -697,6 +720,7 @@ module.exports = React.createClass({
                     room_id: roomString,
                     event_id: eventId,
                     invite_sign_url: params.signurl,
+                    oob_data: oob_data,
                 });
             }
         }
@@ -884,6 +908,7 @@ module.exports = React.createClass({
                             roomId={this.state.currentRoom}
                             eventId={this.state.initialEventId}
                             inviteSignUrl={this.state.inviteSignUrl}
+                            oobData={this.state.roomOobData}
                             highlightedEventId={this.state.highlightedEventId}
                             eventPixelOffset={this.state.initialEventPixelOffset}
                             key={this.state.currentRoom}
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 0ad372634a..3cd933eaaa 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -59,6 +59,17 @@ module.exports = React.createClass({
         // (given as part of the link in the invite email)
         inviteSignUrl: React.PropTypes.string,
 
+        // Any data about the room that would normally come from the Home Server
+        // but has been passed out-of-band, eg. the room name and avatar URL
+        // from an email invite (a workaround for the fact that we can't
+        // get this information from the HS using an email invite).
+        // Fields:
+        //  * name (string) The room's name
+        //  * avatarUrl (string) The mxc:// avatar URL for the room
+        //  * inviterName (string) The display name of the person who
+        //  *                      invited us tovthe room
+        oobData: React.PropTypes.object,
+
         // id of an event to jump to. If not given, will go to the end of the
         // live timeline.
         eventId: React.PropTypes.string,
@@ -1052,13 +1063,19 @@ module.exports = React.createClass({
                     );                
                 }
                 else {
+                    var inviterName = undefined;
+                    if (this.props.oobData) {
+                        inviterName = this.props.oobData.inviterName;
+                    }
+
                     return (
                         <div className="mx_RoomView">
-                            <RoomHeader ref="header" room={this.state.room} simpleHeader="Join room"/>
+                            <RoomHeader ref="header" room={this.state.room} oobData={this.props.oobData} />
                             <div className="mx_RoomView_auxPanel">
                                 <RoomPreviewBar onJoinClick={ this.onJoinButtonClicked } 
                                                 canJoin={ true } canPreview={ false }
                                                 spinner={this.state.joining}
+                                                inviterName={inviterName}
                                 />
                             </div>
                             <div className="mx_RoomView_messagePanel"></div>
@@ -1293,6 +1310,7 @@ module.exports = React.createClass({
         return (
             <div className={ "mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "") } ref="roomView">
                 <RoomHeader ref="header" room={this.state.room} searchInfo={searchInfo}
+                    oobData={this.props.oobData}
                     editing={this.state.editingRoomSettings}
                     onSearchClick={this.onSearchClick}
                     onSettingsClick={this.onSettingsClick}
diff --git a/src/components/views/avatars/RoomAvatar.js b/src/components/views/avatars/RoomAvatar.js
index eed4070fd8..3bae15bd15 100644
--- a/src/components/views/avatars/RoomAvatar.js
+++ b/src/components/views/avatars/RoomAvatar.js
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 var React = require('react');
+var ContentRepo = require("matrix-js-sdk").ContentRepo;
 var MatrixClientPeg = require('../../../MatrixClientPeg');
 var Avatar = require('../../../Avatar');
 var sdk = require("../../../index");
@@ -21,8 +22,12 @@ var sdk = require("../../../index");
 module.exports = React.createClass({
     displayName: 'RoomAvatar',
 
+    // Room may be left unset here, but if it is,
+    // oobData.avatarUrl should be set (else there
+    // would be nowhere to get the avatar from)
     propTypes: {
-        room: React.PropTypes.object.isRequired,
+        room: React.PropTypes.object,
+        oobData: React.PropTypes.object,
         width: React.PropTypes.number,
         height: React.PropTypes.number,
         resizeMethod: React.PropTypes.string
@@ -32,7 +37,8 @@ module.exports = React.createClass({
         return {
             width: 36,
             height: 36,
-            resizeMethod: 'crop'
+            resizeMethod: 'crop',
+            oobData: {},
         }
     },
 
@@ -50,7 +56,12 @@ module.exports = React.createClass({
 
     getImageUrls: function(props) {
         return [
-            this.getRoomAvatarUrl(props), // highest priority
+            ContentRepo.getHttpUriForMxc(
+                MatrixClientPeg.get().getHomeserverUrl(),
+                props.oobData.avatarUrl,
+                props.width, props.height, props.resizeMethod
+            ), // highest priority
+            this.getRoomAvatarUrl(props),
             this.getOneToOneAvatar(props),
             this.getFallbackAvatar(props) // lowest priority
         ].filter(function(url) {
@@ -59,6 +70,8 @@ module.exports = React.createClass({
     },
 
     getRoomAvatarUrl: function(props) {
+        if (!this.props.room) return null;
+
         return props.room.getAvatarUrl(
             MatrixClientPeg.get().getHomeserverUrl(),
             props.width, props.height, props.resizeMethod,
@@ -67,6 +80,8 @@ module.exports = React.createClass({
     },
 
     getOneToOneAvatar: function(props) {
+        if (!this.props.room) return null;
+
         var mlist = props.room.currentState.members;
         var userIds = [];
         // for .. in optimisation to return early if there are >2 keys
@@ -103,14 +118,20 @@ module.exports = React.createClass({
     },
 
     getFallbackAvatar: function(props) {
+        if (!this.props.room) return null;
+
         return Avatar.defaultAvatarUrlForString(props.room.roomId);
     },
 
     render: function() {
         var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
+
+        var roomName = this.props.room ? this.props.room.name : this.props.oobData.name;
+
         return (
-            <BaseAvatar {...this.props} name={this.props.room.name}
-                idName={this.props.room.roomId} urls={this.state.urls} />
+            <BaseAvatar {...this.props} name={roomName}
+                idName={this.props.room ? this.props.room.roomId : null}
+                urls={this.state.urls} />
         );
     }
 });
diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js
index 0b52281507..1b0d7ff491 100644
--- a/src/components/views/rooms/RoomHeader.js
+++ b/src/components/views/rooms/RoomHeader.js
@@ -33,6 +33,7 @@ module.exports = React.createClass({
 
     propTypes: {
         room: React.PropTypes.object,
+        oobData: React.PropTypes.object,
         editing: React.PropTypes.bool,
         onSettingsClick: React.PropTypes.func,
         onSaveClick: React.PropTypes.func,
@@ -217,18 +218,27 @@ module.exports = React.createClass({
                 }
 
                 // XXX: this is a bit inefficient - we could just compare room.name for 'Empty room'...
-                var members = this.props.room.getJoinedMembers();
                 var settingsHint = false;
-                if (members.length === 1 && members[0].userId === MatrixClientPeg.get().credentials.userId) {
-                    var name = this.props.room.currentState.getStateEvents('m.room.name', '');
-                    if (!name || !name.getContent().name) {
-                        settingsHint = true;
+                var members = this.props.room ? this.props.room.getJoinedMembers() : undefined;
+                if (members) {
+                    if (members.length === 1 && members[0].userId === MatrixClientPeg.get().credentials.userId) {
+                        var name = this.props.room.currentState.getStateEvents('m.room.name', '');
+                        if (!name || !name.getContent().name) {
+                            settingsHint = true;
+                        }
                     }
                 }
 
+                var roomName = 'Join Room';
+                if (this.props.oobData && this.props.oobData.name) {
+                    roomName = this.props.oobData.name;
+                } else if (this.props.room) {
+                    roomName = this.props.room.name;
+                }
+
                 name =
                     <div className="mx_RoomHeader_name" onClick={this.props.onSettingsClick}>
-                        <div className={ "mx_RoomHeader_nametext " + (settingsHint ? "mx_RoomHeader_settingsHint" : "") } title={ this.props.room.name }>{ this.props.room.name }</div>
+                        <div className={ "mx_RoomHeader_nametext " + (settingsHint ? "mx_RoomHeader_settingsHint" : "") } title={ roomName }>{ roomName }</div>
                         { searchStatus }
                         <div className="mx_RoomHeader_settingsButton" title="Settings">
                             <TintableSvg src="img/settings.svg" width="12" height="12"/>
@@ -246,36 +256,34 @@ module.exports = React.createClass({
                          onValueChanged={ this.onTopicChanged }
                          initialValue={ this.state.topic }/>
             } else {
-                var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
+                var topic = this.props.room ? this.props.room.currentState.getStateEvents('m.room.topic', '') : '';
                 if (topic) topic_el = <div className="mx_RoomHeader_topic" ref="topic" title={ topic.getContent().topic }>{ topic.getContent().topic }</div>;
             }
 
             var roomAvatar = null;
-            if (this.props.room) {
-                if (can_set_room_avatar) {
-                    roomAvatar = (
-                        <div className="mx_RoomHeader_avatarPicker">
-                            <div onClick={ this.onAvatarPickerClick }>
-                                <ChangeAvatar ref="changeAvatar" room={this.props.room} showUploadSection={false} width={48} height={48} />
-                            </div>
-                            <div className="mx_RoomHeader_avatarPicker_edit">
-                                <label htmlFor="avatarInput" ref="file_label">
-                                    <img src="img/camera.svg"
-                                        alt="Upload avatar" title="Upload avatar"
-                                        width="17" height="15" />
-                                </label>
-                                <input id="avatarInput" type="file" onChange={ this.onAvatarSelected }/>
-                            </div>
+            if (can_set_room_avatar) {
+                roomAvatar = (
+                    <div className="mx_RoomHeader_avatarPicker">
+                        <div onClick={ this.onAvatarPickerClick }>
+                            <ChangeAvatar ref="changeAvatar" room={this.props.room} showUploadSection={false} width={48} height={48} />
                         </div>
-                    );
-                }
-                else {
-                    roomAvatar = (
-                        <div onClick={this.props.onSettingsClick}>
-                            <RoomAvatar room={this.props.room} width={48} height={48}/>
+                        <div className="mx_RoomHeader_avatarPicker_edit">
+                            <label htmlFor="avatarInput" ref="file_label">
+                                <img src="img/camera.svg"
+                                    alt="Upload avatar" title="Upload avatar"
+                                    width="17" height="15" />
+                            </label>
+                            <input id="avatarInput" type="file" onChange={ this.onAvatarSelected }/>
                         </div>
-                    );
-                }
+                    </div>
+                );
+            }
+            else if (this.props.room || (this.props.oobData && this.props.oobData.name)) {
+                roomAvatar = (
+                    <div onClick={this.props.onSettingsClick}>
+                        <RoomAvatar room={this.props.room} width={48} height={48} oobData={this.props.oobData} />
+                    </div>
+                );
             }
 
             var leave_button;