diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js
index d52bbbb0d0..dc3893785d 100644
--- a/src/components/views/rooms/RoomPreviewBar.js
+++ b/src/components/views/rooms/RoomPreviewBar.js
@@ -26,6 +26,8 @@ import classNames from 'classnames';
 import { _t } from '../../../languageHandler';
 import SdkConfig from "../../../SdkConfig";
 import IdentityAuthClient from '../../../IdentityAuthClient';
+import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore";
+import {UPDATE_EVENT} from "../../../stores/AsyncStore";
 
 const MessageCase = Object.freeze({
     NotLoggedIn: "NotLoggedIn",
@@ -100,6 +102,7 @@ export default createReactClass({
 
     componentDidMount: function() {
         this._checkInvitedEmail();
+        CommunityPrototypeStore.instance.on(UPDATE_EVENT, this._onCommunityUpdate);
     },
 
     componentDidUpdate: function(prevProps, prevState) {
@@ -108,6 +111,10 @@ export default createReactClass({
         }
     },
 
+    componentWillUnmount: function() {
+        CommunityPrototypeStore.instance.off(UPDATE_EVENT, this._onCommunityUpdate);
+    },
+
     _checkInvitedEmail: async function() {
         // If this is an invite and we've been told what email address was
         // invited, fetch the user's account emails and discovery bindings so we
@@ -143,6 +150,13 @@ export default createReactClass({
         }
     },
 
+    _onCommunityUpdate: function (roomId) {
+        if (this.props.room && this.props.room.roomId !== roomId) {
+            return;
+        }
+        this.forceUpdate(); // we have nothing to update
+    },
+
     _getMessageCase() {
         const isGuest = MatrixClientPeg.get().isGuest();
 
@@ -219,8 +233,15 @@ export default createReactClass({
         }
     },
 
+    _communityProfile: function() {
+        if (this.props.room) return CommunityPrototypeStore.instance.getInviteProfile(this.props.room.roomId);
+        return {displayName: null, avatarMxc: null};
+    },
+
     _roomName: function(atStart = false) {
-        const name = this.props.room ? this.props.room.name : this.props.roomAlias;
+        let name = this.props.room ? this.props.room.name : this.props.roomAlias;
+        const profile = this._communityProfile();
+        if (profile.displayName) name = profile.displayName;
         if (name) {
             return name;
         } else if (atStart) {
@@ -439,7 +460,10 @@ export default createReactClass({
             }
             case MessageCase.Invite: {
                 const RoomAvatar = sdk.getComponent("views.avatars.RoomAvatar");
-                const avatar = <RoomAvatar room={this.props.room} oobData={this.props.oobData} />;
+                const oobData = Object.assign({}, this.props.oobData, {
+                    avatarUrl: this._communityProfile().avatarMxc,
+                });
+                const avatar = <RoomAvatar room={this.props.room} oobData={oobData} />;
 
                 const inviteMember = this._getInviteMember();
                 let inviterElement;
diff --git a/src/components/views/rooms/RoomTile.tsx b/src/components/views/rooms/RoomTile.tsx
index 0c99b98e1a..a09853d762 100644
--- a/src/components/views/rooms/RoomTile.tsx
+++ b/src/components/views/rooms/RoomTile.tsx
@@ -27,7 +27,7 @@ import defaultDispatcher from '../../../dispatcher/dispatcher';
 import { Key } from "../../../Keyboard";
 import ActiveRoomObserver from "../../../ActiveRoomObserver";
 import { _t } from "../../../languageHandler";
-import { ChevronFace, ContextMenuTooltipButton, MenuItemRadio } from "../../structures/ContextMenu";
+import { ChevronFace, ContextMenuTooltipButton } from "../../structures/ContextMenu";
 import { DefaultTagID, TagID } from "../../../stores/room-list/models";
 import { MessagePreviewStore, ROOM_PREVIEW_CHANGED } from "../../../stores/room-list/MessagePreviewStore";
 import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
@@ -47,8 +47,11 @@ import { PROPERTY_UPDATED } from "../../../stores/local-echo/GenericEchoChamber"
 import IconizedContextMenu, {
     IconizedContextMenuCheckbox,
     IconizedContextMenuOption,
-    IconizedContextMenuOptionList, IconizedContextMenuRadio
+    IconizedContextMenuOptionList,
+    IconizedContextMenuRadio
 } from "../context_menus/IconizedContextMenu";
+import { CommunityPrototypeStore, IRoomProfile } from "../../../stores/CommunityPrototypeStore";
+import { UPDATE_EVENT } from "../../../stores/AsyncStore";
 
 interface IProps {
     room: Room;
@@ -101,6 +104,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
         this.notificationState.on(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate);
         this.roomProps = EchoChamber.forRoom(this.props.room);
         this.roomProps.on(PROPERTY_UPDATED, this.onRoomPropertyUpdate);
+        CommunityPrototypeStore.instance.on(UPDATE_EVENT, this.onCommunityUpdate);
     }
 
     private onNotificationUpdate = () => {
@@ -140,6 +144,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
         defaultDispatcher.unregister(this.dispatcherRef);
         MessagePreviewStore.instance.off(ROOM_PREVIEW_CHANGED, this.onRoomPreviewChanged);
         this.notificationState.off(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate);
+        CommunityPrototypeStore.instance.off(UPDATE_EVENT, this.onCommunityUpdate);
     }
 
     private onAction = (payload: ActionPayload) => {
@@ -150,6 +155,11 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
         }
     };
 
+    private onCommunityUpdate = (roomId: string) => {
+        if (roomId !== this.props.room.roomId) return;
+        this.forceUpdate(); // we don't have anything to actually update
+    };
+
     private onRoomPreviewChanged = (room: Room) => {
         if (this.props.room && room.roomId === this.props.room.roomId) {
             // generatePreview() will return nothing if the user has previews disabled
@@ -461,11 +471,21 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
             'mx_RoomTile_minimized': this.props.isMinimized,
         });
 
+        let roomProfile: IRoomProfile = {displayName: null, avatarMxc: null};
+        if (this.props.tag === DefaultTagID.Invite) {
+            roomProfile = CommunityPrototypeStore.instance.getInviteProfile(this.props.room.roomId);
+        }
+
+        let name = roomProfile.displayName || this.props.room.name;
+        if (typeof name !== 'string') name = '';
+        name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
+
         const roomAvatar = <DecoratedRoomAvatar
             room={this.props.room}
             avatarSize={32}
             tag={this.props.tag}
             displayBadge={this.props.isMinimized}
+            oobData={({avatarUrl: roomProfile.avatarMxc})}
         />;
 
         let badge: React.ReactNode;
@@ -482,10 +502,6 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
             );
         }
 
-        let name = this.props.room.name;
-        if (typeof name !== 'string') name = '';
-        name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
-
         let messagePreview = null;
         if (this.showMessagePreview && this.state.messagePreview) {
             messagePreview = (
diff --git a/src/stores/CommunityPrototypeStore.ts b/src/stores/CommunityPrototypeStore.ts
new file mode 100644
index 0000000000..581f8a97c8
--- /dev/null
+++ b/src/stores/CommunityPrototypeStore.ts
@@ -0,0 +1,100 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
+import defaultDispatcher from "../dispatcher/dispatcher";
+import { ActionPayload } from "../dispatcher/payloads";
+import { Room } from "matrix-js-sdk/src/models/room";
+import { EffectiveMembership, getEffectiveMembership } from "../utils/membership";
+import SettingsStore from "../settings/SettingsStore";
+import * as utils from "matrix-js-sdk/src/utils";
+import { UPDATE_EVENT } from "./AsyncStore";
+
+interface IState {
+    // nothing of value - we use account data
+}
+
+export interface IRoomProfile {
+    displayName: string;
+    avatarMxc: string;
+}
+
+export class CommunityPrototypeStore extends AsyncStoreWithClient<IState> {
+    private static internalInstance = new CommunityPrototypeStore();
+
+    private constructor() {
+        super(defaultDispatcher, {});
+    }
+
+    public static get instance(): CommunityPrototypeStore {
+        return CommunityPrototypeStore.internalInstance;
+    }
+
+    protected async onAction(payload: ActionPayload): Promise<any> {
+        if (!this.matrixClient || !SettingsStore.getValue("feature_communities_v2_prototypes")) {
+            return;
+        }
+
+        if (payload.action === "MatrixActions.Room.myMembership") {
+            const room: Room = payload.room;
+            const membership = getEffectiveMembership(payload.membership);
+            const oldMembership = getEffectiveMembership(payload.oldMembership);
+            if (membership === oldMembership) return;
+
+            if (membership === EffectiveMembership.Invite) {
+                try {
+                    const path = utils.encodeUri("/rooms/$roomId/group_info", {$roomId: room.roomId});
+                    const profile = await this.matrixClient._http.authedRequest(
+                        undefined, "GET", path,
+                        undefined, undefined,
+                        {prefix: "/_matrix/client/unstable/im.vector.custom"});
+                    // we use global account data because per-room account data on invites is unreliable
+                    await this.matrixClient.setAccountData("im.vector.group_info." + room.roomId, profile);
+                } catch (e) {
+                    console.warn("Non-fatal error getting group information for invite:", e);
+                }
+            }
+        } else if (payload.action === "MatrixActions.accountData") {
+            if (payload.event_type.startsWith("im.vector.group_info.")) {
+                this.emit(UPDATE_EVENT, payload.event_type.substring("im.vector.group_info.".length));
+            }
+        }
+    }
+
+    public getInviteProfile(roomId: string): IRoomProfile {
+        if (!this.matrixClient) return {displayName: null, avatarMxc: null};
+        const room = this.matrixClient.getRoom(roomId);
+        if (SettingsStore.getValue("feature_communities_v2_prototypes")) {
+            const data = this.matrixClient.getAccountData("im.vector.group_info." + roomId);
+            if (data && data.getContent()) {
+                return {displayName: data.getContent().name, avatarMxc: data.getContent().avatar_url};
+            }
+        }
+        return {displayName: room.name, avatarMxc: room.avatar_url};
+    }
+
+    protected async onReady(): Promise<any> {
+        for (const room of this.matrixClient.getRooms()) {
+            const myMember = room.currentState.getMembers().find(m => m.userId === this.matrixClient.getUserId());
+            if (!myMember) continue;
+            if (getEffectiveMembership(myMember.membership) === EffectiveMembership.Invite) {
+                // Fake an update for anything that might have started listening before the invite
+                // data was available (eg: RoomPreviewBar after a refresh)
+                this.emit(UPDATE_EVENT, room.roomId);
+            }
+        }
+    }
+}