diff --git a/src/App.tsx b/src/App.tsx
index 297f4df..78eec8c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -53,6 +53,7 @@ class App extends React.Component<AppProps, AppState> {
 		this.setState({
 			...this.state,
 			account: this.account(),
+			email: this.client.email(emailId),
 			location: {
 				accountId: accountId,
 				emailId: emailId,
@@ -107,6 +108,7 @@ class App extends React.Component<AppProps, AppState> {
 				accounts: this.client.state.session
 					? this.client.state.session.accounts
 					: {},
+				email: this.client.email(this.state.location.emailId),
 				mailbox: this.mailbox(),
 			});
 		});
@@ -130,6 +132,7 @@ class App extends React.Component<AppProps, AppState> {
 					accounts={this.state.accounts}
 					client={this.client}
 					email={this.state.email}
+					emailId={this.state.location.emailId}
 					mailbox={this.state.mailbox}
 				/>
 				<AuthModal
diff --git a/src/AppLayout.tsx b/src/AppLayout.tsx
index b9b6921..c3409f8 100644
--- a/src/AppLayout.tsx
+++ b/src/AppLayout.tsx
@@ -4,7 +4,7 @@ import Row from "react-bootstrap/Row";
 import Col from "react-bootstrap/Col";
 
 import AccountList from "./AccountList";
-import EmailList from "./EmailList";
+import EmailArea from "./EmailArea";
 import MailboxList from "./MailboxList";
 import Client from "./client/Client";
 import { AccountIdMap, IAccount, IEmail, IMailbox } from "./client/types";
@@ -14,6 +14,7 @@ type TopProps = {
 	accounts: AccountIdMap;
 	client: Client;
 	email: IEmail | null;
+	emailId: string;
 	mailbox: IMailbox | null;
 };
 
@@ -32,9 +33,11 @@ const AppLayout: React.FC<TopProps> = (props) => {
 					<MailboxList account={props.account} client={props.client} />
 				</Col>
 				<Col lg="11">
-					<EmailList
+					<EmailArea
 						account={props.account}
 						client={props.client}
+						email={props.email}
+						emailId={props.emailId}
 						mailbox={props.mailbox}
 					/>
 				</Col>
diff --git a/src/EmailArea.tsx b/src/EmailArea.tsx
new file mode 100644
index 0000000..3d3c049
--- /dev/null
+++ b/src/EmailArea.tsx
@@ -0,0 +1,44 @@
+import React from "react";
+import Stack from "react-bootstrap/Stack";
+
+import { IAccount, IEmail, IMailbox } from "./client/types";
+import Client from "./client/Client";
+import EmailContent from "./EmailContent";
+import EmailList from "./EmailList";
+
+type EmailAreaProps = {
+	account: IAccount | null;
+	client: Client;
+	email: IEmail | null;
+	emailId: string;
+	mailbox: IMailbox | null;
+};
+
+const EmailArea: React.FC<EmailAreaProps> = (props) => {
+	if (props.emailId === "") {
+		return (
+			<EmailList
+				account={props.account}
+				client={props.client}
+				mailbox={props.mailbox}
+			/>
+		);
+	} else {
+		return (
+			<Stack className="text-start">
+				<EmailList
+					account={props.account}
+					client={props.client}
+					mailbox={props.mailbox}
+				/>
+				<EmailContent
+					account={props.account}
+					client={props.client}
+					email={props.email}
+					emailId={props.emailId}
+				/>
+			</Stack>
+		);
+	}
+};
+export default EmailArea;
diff --git a/src/EmailContent.tsx b/src/EmailContent.tsx
new file mode 100644
index 0000000..6506fdf
--- /dev/null
+++ b/src/EmailContent.tsx
@@ -0,0 +1,44 @@
+import React from "react";
+import Placeholder from "react-bootstrap/Placeholder";
+
+import Client from "./client/Client";
+import { IAccount, IEmail } from "./client/types";
+
+type EmailContentProps = {
+	account: IAccount | null;
+	client: Client | null;
+	email: IEmail | null;
+	emailId: string;
+};
+
+type EmailContentState = {};
+
+class EmailContent extends React.Component<
+	EmailContentProps,
+	EmailContentState
+> {
+	componentDidMount() {
+		this.ensureData();
+	}
+	componentDidUpdate() {
+		this.ensureData();
+	}
+
+	ensureData() {
+		if (this.props.account == null) return;
+		if (this.props.client == null) return;
+		this.props.client.ensureEmailContent(
+			this.props.account.id,
+			this.props.emailId,
+		);
+	}
+
+	render() {
+		if (this.props.email == null) {
+			return <Placeholder />;
+		} else {
+			return <p>{this.props.email.preview}</p>;
+		}
+	}
+}
+export default EmailContent;
diff --git a/src/EmailList.tsx b/src/EmailList.tsx
index fc9151a..69ebd3d 100644
--- a/src/EmailList.tsx
+++ b/src/EmailList.tsx
@@ -40,7 +40,7 @@ class EmailList extends React.Component<EmailListProps, EmailListState> {
 							account={this.props.account}
 							client={this.props.client}
 							emailId={e}
-							email={this.props.client!.email(e)}
+							emailStub={this.props.client!.emailStub(e)}
 							key={e}
 							mailbox={this.props.mailbox}
 						/>
diff --git a/src/EmailSummary.tsx b/src/EmailSummary.tsx
index 5520ba1..f777e4a 100644
--- a/src/EmailSummary.tsx
+++ b/src/EmailSummary.tsx
@@ -2,13 +2,13 @@ import React from "react";
 import Placeholder from "react-bootstrap/Placeholder";
 
 import Client from "./client/Client";
-import { IAccount, IEmail, IMailbox } from "./client/types";
+import { IAccount, IEmailStub, IMailbox } from "./client/types";
 
 type EmailSummaryProps = {
 	account: IAccount | null;
 	client: Client | null;
-	email: IEmail | null;
 	emailId: string;
+	emailStub: IEmailStub | null;
 	mailbox: IMailbox | null;
 };
 type EmailSummaryState = {};
@@ -27,7 +27,10 @@ class EmailSummary extends React.Component<
 	ensureData() {
 		if (this.props.account == null) return;
 		if (this.props.client == null) return;
-		this.props.client.ensureEmailGet(this.props.account.id, this.props.emailId);
+		this.props.client.ensureEmailStub(
+			this.props.account.id,
+			this.props.emailId,
+		);
 	}
 
 	render() {
@@ -48,8 +51,8 @@ class EmailSummary extends React.Component<
 		return (
 			<div className="p-2 border" key={this.props.emailId}>
 				<a className="btn" href={href}>
-					{this.props.email != null
-						? this.props.email.subject
+					{this.props.emailStub != null
+						? this.props.emailStub.subject
 						: this.props.emailId}
 				</a>
 			</div>
diff --git a/src/client/Client.tsx b/src/client/Client.tsx
index e96c6e8..368ba74 100644
--- a/src/client/Client.tsx
+++ b/src/client/Client.tsx
@@ -6,7 +6,7 @@ import * as base64 from "base-64";
 import * as jmapclient from "jmap-client-ts";
 import { FetchTransport } from "jmap-client-ts/lib/utils/fetch-transport";
 
-import { IAccount, IEmail, IMailbox, ISession } from "./types";
+import { IAccount, IEmail, IEmailStub, IMailbox, ISession } from "./types";
 
 type Callback = () => void;
 
@@ -16,45 +16,31 @@ export interface IAuth {
 }
 
 export interface ClientState {
+	inFlight: {
+		emailContents: Set<string>;
+		emailStubs: Set<string>;
+		mailboxes: Set<string>;
+	};
 	session: ISession | null;
 }
 
 export default class Client {
-	// Get the currently active account
-	account(accountId: string): IAccount | null {
-		if (!(this.state.session && this.state.session.accounts)) return null;
-		return this.state.session.accounts[accountId];
-	}
 	// All objects which currently are listening for changes
 	callbacks: Array<Callback> = [];
 	jclient: jmapclient.Client | null = null;
 	state: ClientState = {
+		inFlight: {
+			emailContents: new Set(),
+			emailStubs: new Set(),
+			mailboxes: new Set(),
+		},
 		session: null,
 	};
 
-	onChange(f: Callback) {
-		this.callbacks.push(f);
-	}
-
-	// Ensure we have the fully-fleshed email
-	ensureEmailGet(accountId: string, emailId: string) {
-		if (this.state.session == null) return;
-		const existing = this.state.session.emails[emailId];
-		if (existing != null) return;
-		this.emailGet(accountId, emailId);
-	}
-
-	// Ensure we have the list of emails for the provided account and mailbox
-	ensureEmailList(accountId: string, mailboxId: string) {
-		const mailbox = this.mailbox(accountId, mailboxId);
-		if (mailbox != null && mailbox.emailIds != null) return;
-		this.emailList(accountId, mailboxId, []);
-	}
-
-	// Ensure that login happens. If this is called many times, only login once.
-	ensureLogin(auth: IAuth) {
-		if (this.jclient != null) return;
-		this.doLogin(auth);
+	// Get the currently active account
+	account(accountId: string): IAccount | null {
+		if (!(this.state.session && this.state.session.accounts)) return null;
+		return this.state.session.accounts[accountId];
 	}
 
 	// Make the request to get system metadata
@@ -81,36 +67,110 @@ export default class Client {
 		return;
 	}
 
+	// Ensure we have the full email content
+	ensureEmailContent(accountId: string, emailId: string) {
+		if (this.state.session == null) return;
+		const existing = this.state.session.emails[emailId];
+		if (existing != null) return;
+		this.emailGetContent(accountId, emailId);
+	}
+
+	// Ensure we have the email summary
+	ensureEmailStub(accountId: string, emailId: string) {
+		if (this.state.session == null) return;
+		const existing = this.state.session.emailStubs[emailId];
+		if (existing != null) return;
+		this.emailGetStub(accountId, emailId);
+	}
+
+	// Ensure we have the list of emails for the provided account and mailbox
+	ensureEmailList(accountId: string, mailboxId: string) {
+		const mailbox = this.mailbox(accountId, mailboxId);
+		if (mailbox != null && mailbox.emailIds != null) return;
+		this.emailList(accountId, mailboxId, []);
+	}
+
+	// Ensure that login happens. If this is called many times, only login once.
+	ensureLogin(auth: IAuth) {
+		if (this.jclient != null) return;
+		this.doLogin(auth);
+	}
+
 	email(emailId: string): IEmail | null {
 		if (this.state.session == null) return null;
 		return this.state.session.emails[emailId];
 	}
 
-	emailGet(accountId: string, emailId: string) {
+	emailGetContent(accountId: string, emailId: string) {
 		if (this.jclient == null) return;
 
+		if (this.state.session == null) return;
+		if (this.state.inFlight.emailContents.has(emailId)) return;
+		this.state.inFlight.emailContents.add(emailId);
+		const msg = "Email get content";
 		this.jclient
 			.email_get({
 				accountId: accountId,
 				ids: [emailId],
-				properties: ["mailboxIds", "subject"],
 			})
 			.then((response) => {
-				console.log("Email response", response);
+				console.log(msg, "response", response);
 				response.list.forEach((e) => {
-					if (this.state.session == null) return;
-					const existing = this.state.session.emails[e.id];
+					const existing = this.state.session!.emails[e.id];
 					if (existing !== undefined && existing.subject !== e.subject) {
 						console.error(
 							"Expectations violation: email ID is not unique within the server!",
 						);
 					}
-					this.state.session.emails[e.id] = e;
-					this._triggerChange("Email " + e.id);
+					this.state.session!.emails[e.id] = {
+						...e,
+						id: e.id,
+					};
+					this._triggerChange(msg + e.id);
 				});
 			})
 			.catch((x) => {
-				console.error("Failed to get email", emailId, x);
+				console.error("Failed to get email content", emailId, x);
+			})
+			.finally(() => {
+				this.state.inFlight.emailContents.delete(emailId);
+			});
+	}
+
+	emailGetStub(accountId: string, emailId: string) {
+		if (this.jclient == null) return;
+		if (this.state.session == null) return;
+		if (this.state.inFlight.emailStubs.has(emailId)) return;
+		this.state.inFlight.emailStubs.add(emailId);
+		const msg = "Email get summary";
+		this.jclient
+			.email_get({
+				accountId: accountId,
+				ids: [emailId],
+				properties: ["subject"],
+			})
+			.then((response) => {
+				console.log(msg, "response", response);
+				response.list.forEach((e) => {
+					if (this.state.session == null) return;
+					const existing = this.state.session.emailStubs[e.id];
+					if (existing !== undefined && existing.subject !== e.subject) {
+						console.error(
+							"Expectations violation: email ID is not unique within the server!",
+						);
+					}
+					this.state.session.emailStubs[e.id] = {
+						id: e.id,
+						subject: e.subject,
+					};
+					this._triggerChange(msg + e.id);
+				});
+			})
+			.catch((x) => {
+				console.error("Failed to get email stub", emailId, x);
+			})
+			.finally(() => {
+				this.state.inFlight.emailStubs.delete(emailId);
 			});
 	}
 
@@ -132,6 +192,11 @@ export default class Client {
 			});
 	}
 
+	emailStub(emailId: string): IEmailStub | null {
+		if (this.state.session == null) return null;
+		return this.state.session.emailStubs[emailId];
+	}
+
 	mailbox(accountId: string, mailboxId: string): IMailbox | null {
 		if (this.state.session == null) return null;
 		const account = this.state.session.accounts[accountId];
@@ -161,7 +226,6 @@ export default class Client {
 					mailboxes.push({
 						...m,
 						emailIds: null,
-						emails: null,
 					});
 				});
 				account.mailboxes = mailboxes;
@@ -169,6 +233,10 @@ export default class Client {
 			});
 	}
 
+	onChange(f: Callback) {
+		this.callbacks.push(f);
+	}
+
 	_triggerChange(msg: string) {
 		console.log("Client change", msg);
 		this.callbacks.forEach((c) => {
@@ -191,6 +259,7 @@ export default class Client {
 				]),
 			),
 			emails: {},
+			emailStubs: {},
 		};
 		if (!this.state.session) return;
 		this._triggerChange("Session");
diff --git a/src/client/types.tsx b/src/client/types.tsx
index 08b9cf9..3906350 100644
--- a/src/client/types.tsx
+++ b/src/client/types.tsx
@@ -1,8 +1,12 @@
 import client from "jmap-client-ts/lib/types";
 
+export interface IEmailStub {
+	id: string;
+	subject: string;
+}
+
 export interface IMailbox extends client.IMailboxProperties {
 	emailIds: Array<string> | null;
-	emails: Array<client.IEmailProperties> | null;
 }
 
 export interface IEmail extends client.IEmailProperties {}
@@ -13,9 +17,11 @@ export interface IAccount extends client.IAccount {
 }
 
 export type AccountIdMap = { [accountId: string]: IAccount };
+export type EmailStubIdMap = { [emailId: string]: IEmailStub };
 export type EmailIdMap = { [emailId: string]: IEmail };
 
 export interface ISession extends client.ISession {
 	accounts: AccountIdMap;
 	emails: EmailIdMap;
+	emailStubs: EmailStubIdMap;
 }