diff --git a/src/components/ClientList.scss b/src/components/ClientList.scss
new file mode 100644
index 0000000000000000000000000000000000000000..24c882309fc63386edfa9eaf6aa7ddf8778ec3af
--- /dev/null
+++ b/src/components/ClientList.scss
@@ -0,0 +1,22 @@
+/*
+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.
+*/
+
+.clientList {
+    display: grid;
+    row-gap: 20px;
+    list-style-type: none;
+    padding-inline-start: 0;
+}
diff --git a/src/components/ClientList.tsx b/src/components/ClientList.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3fdb7456bf14e4aaf023ae9bf9f61ed7f7c936a7
--- /dev/null
+++ b/src/components/ClientList.tsx
@@ -0,0 +1,91 @@
+/*
+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 React, { useContext } from 'react';
+import { UAContext } from '@quentin-sommer/react-useragent';
+
+import { SafeLink } from '../parser/types';
+import { ActionType, ClientContext } from '../contexts/ClientContext';
+import Clients from '../clients';
+import { Client, Platform } from '../clients/types';
+import ClientTile from './ClientTile';
+
+import './ClientList.scss';
+
+interface IProps {
+    link: SafeLink;
+}
+
+const ClientList: React.FC<IProps> = ({ link }: IProps) => {
+    const [
+        { rememberSelection, showOnlyDeviceClients, showExperimentalClients },
+        clientDispatcher,
+    ] = useContext(ClientContext);
+    const { uaResults } = useContext(UAContext);
+
+    /*
+     * Function to decide whether a client is shown
+     */
+    const showClient = (client: Client): boolean => {
+        let showClient = false;
+
+        if (!showOnlyDeviceClients || uaResults === {}) {
+            showClient = true;
+        }
+
+        switch (client.platform) {
+            case Platform.Desktop:
+                showClient = showClient || !(uaResults as any).mobile;
+                break;
+            case Platform.iOS:
+                showClient = showClient || (uaResults as any).ios;
+                break;
+            case Platform.Android:
+                showClient = showClient || (uaResults as any).android;
+                break;
+        }
+
+        if (!showExperimentalClients && client.experimental) {
+            showClient = false;
+        }
+
+        return showClient;
+    };
+
+    const clientLi = (client: Client): JSX.Element => (
+        <li
+            key={client.clientId}
+            onClick={(): void =>
+                rememberSelection
+                    ? clientDispatcher({
+                          action: ActionType.SetClient,
+                          clientId: client.clientId,
+                      })
+                    : undefined
+            }
+        >
+            <ClientTile client={client} link={link} />
+        </li>
+    );
+
+    return (
+        <ul className="clientList">
+            {Clients.filter(showClient).map(clientLi)}
+        </ul>
+    );
+};
+
+export default ClientList;
diff --git a/src/components/ClientSelection.scss b/src/components/ClientSelection.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3900f7726c835c999f9c2e7a5a51e2c2e3af998f
--- /dev/null
+++ b/src/components/ClientSelection.scss
@@ -0,0 +1,30 @@
+/*
+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.
+*/
+
+.advanced {
+    margin: 0 5%;
+
+    .advancedOptions {
+        display: flex;
+        flex-direction: column;
+
+        align-items: flex-start;
+    }
+
+    .clientList {
+        margin-top: 20px;
+    }
+}
diff --git a/src/components/ClientSelection.tsx b/src/components/ClientSelection.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6a15ad42969c81fc1b5cd4ed01529a4596767c8d
--- /dev/null
+++ b/src/components/ClientSelection.tsx
@@ -0,0 +1,79 @@
+/*
+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 React, { useContext } from 'react';
+
+import './ClientSelection.scss';
+import { ActionType, ClientContext } from '../contexts/ClientContext';
+import ClientList from './ClientList';
+import { SafeLink } from '../parser/types';
+
+interface IProps {
+    link: SafeLink;
+}
+
+const ClientSelection: React.FC<IProps> = ({ link }: IProps) => {
+    const [clientState, clientStateDispatch] = useContext(ClientContext);
+    const options = (
+        <div className="advancedOptions">
+            <label>
+                <input
+                    type="checkbox"
+                    onChange={(): void => {
+                        clientStateDispatch({
+                            action: ActionType.ToggleRememberSelection,
+                        });
+                    }}
+                    checked={clientState.rememberSelection}
+                />
+                Remember my selection for future invites in this browser
+            </label>
+            <label>
+                <input
+                    type="checkbox"
+                    onChange={(): void => {
+                        clientStateDispatch({
+                            action: ActionType.ToggleShowOnlyDeviceClients,
+                        });
+                    }}
+                    checked={clientState.showOnlyDeviceClients}
+                />
+                Show only clients suggested for this device
+            </label>
+            <label>
+                <input
+                    type="checkbox"
+                    onChange={(): void => {
+                        clientStateDispatch({
+                            action: ActionType.ToggleShowExperimentalClients,
+                        });
+                    }}
+                    checked={clientState.showExperimentalClients}
+                />
+                Show experimental clients
+            </label>
+        </div>
+    );
+
+    return (
+        <div className="advanced">
+            {options}
+            <ClientList link={link} />
+        </div>
+    );
+};
+
+export default ClientSelection;
diff --git a/src/components/ClientTile.scss b/src/components/ClientTile.scss
new file mode 100644
index 0000000000000000000000000000000000000000..18a8be053f669ed6a61027d858a3980895165345
--- /dev/null
+++ b/src/components/ClientTile.scss
@@ -0,0 +1,66 @@
+/*
+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 '../color-scheme';
+
+.clientTile {
+    display: flex;
+    flex-direction: row;
+
+    height: 155px;
+    width: 100%;
+
+    color: $foreground;
+
+    > img {
+        flex-shrink: 0;
+        height: 100%;
+    }
+
+    h1 {
+        text-align: left;
+        font-size: 14px;
+        line-height: 24px;
+    }
+
+    p {
+        margin-right: 20px;
+        margin-top: 20px;
+        text-align: left;
+    }
+
+    border: 1px solid $borders;
+    border-radius: 8px;
+
+    padding: 8px;
+
+    // For the chevron
+    position: relative;
+}
+
+.clientTileLink {
+    position: relative;
+
+    width: 100%;
+
+    &::after {
+        // TODO: add chevron top right
+        position: absolute;
+        right: 10px;
+        top: 5px;
+        content: '>';
+    }
+}
diff --git a/src/components/ClientTile.tsx b/src/components/ClientTile.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6367b72bd748507a62cc21fd22fb07cd3c7f3c4a
--- /dev/null
+++ b/src/components/ClientTile.tsx
@@ -0,0 +1,58 @@
+/*
+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 React from 'react';
+import classNames from 'classnames';
+
+import { Client, ClientKind } from '../clients/types';
+import { SafeLink } from '../parser/types';
+import Tile from './Tile';
+
+import './ClientTile.scss';
+
+interface IProps {
+    client: Client;
+    link: SafeLink;
+}
+
+const ClientTile: React.FC<IProps> = ({ client, link }: IProps) => {
+    const inviteLine =
+        client.kind === ClientKind.TEXT_CLIENT ? (
+            <p>{client.toInviteString(link)}</p>
+        ) : null;
+
+    const className = classNames('clientTile', {
+        clientTileLink: client.kind === ClientKind.LINKED_CLIENT,
+    });
+    let clientTile = (
+        <Tile className={className}>
+            <img src={client.logo} alt={client.name + ' logo'} />
+            <div>
+                <h1>{client.name}</h1>
+                <p>{client.description}</p>
+                {inviteLine}
+            </div>
+        </Tile>
+    );
+
+    if (client.kind === ClientKind.LINKED_CLIENT) {
+        clientTile = <a href={client.toUrl(link).toString()}>{clientTile}</a>;
+    }
+
+    return clientTile;
+};
+
+export default ClientTile;