diff --git a/package.json b/package.json
index 792a4133031569f8b2f78f36737c5d1f2579d2c1..ad67d5fdd18a6fa4454e372be22de265f4bdba50 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
     "version": "0.1.0",
     "private": true,
     "dependencies": {
+        "@quentin-sommer/react-useragent": "^3.1.0",
         "classnames": "^2.2.6",
         "formik": "^2.1.4",
         "matrix-cypher": "^0.1.12",
diff --git a/src/App.tsx b/src/App.tsx
index 748d405bf401947cd4cb98d9c819b6c7377db328..e2d9347aaa8978d9d2077e70c5131d26edfb8f70 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -14,34 +14,34 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-import React from "react";
+import React from 'react';
 
-import SingleColumn from "./layouts/SingleColumn";
-import CreateLinkTile from "./components/CreateLinkTile";
-import MatrixTile from "./components/MatrixTile";
-import Tile from "./components/Tile";
-import LinkRouter from "./pages/LinkRouter";
+import SingleColumn from './layouts/SingleColumn';
+import CreateLinkTile from './components/CreateLinkTile';
+import MatrixTile from './components/MatrixTile';
+import Tile from './components/Tile';
+import LinkRouter from './pages/LinkRouter';
 
-import "./App.scss";
+import GlobalContext from './contexts/GlobalContext';
 
 /* eslint-disable no-restricted-globals */
 
 const App: React.FC = () => {
     let page = (
         <>
-            <CreateLinkTile /> <hr />{" "}
+            <CreateLinkTile /> <hr />{' '}
         </>
     );
     if (location.hash) {
         console.log(location.hash);
-        if (location.hash.startsWith("#/")) {
+        if (location.hash.startsWith('#/')) {
             page = <LinkRouter link={location.hash.slice(2)} />;
         } else {
-            console.log("asdfadf");
+            console.log('asdfadf');
             page = (
                 <Tile>
-                    Links should be in the format {location.host}/#/{"<"}
-                    matrix-resource-identifier{">"}
+                    Links should be in the format {location.host}/#/{'<'}
+                    matrix-resource-identifier{'>'}
                 </Tile>
             );
         }
@@ -50,7 +50,7 @@ const App: React.FC = () => {
     return (
         <SingleColumn>
             <div className="topSpacer" />
-            {page}
+            <GlobalContext>{page}</GlobalContext>
             <MatrixTile />
             <div className="bottomSpacer" />
         </SingleColumn>
diff --git a/src/clients/types.ts b/src/clients/types.ts
index 8daea917538390829773df441555a0b2fa719103..2df503be8c9d45d782b437c51e7b737679673c51 100644
--- a/src/clients/types.ts
+++ b/src/clients/types.ts
@@ -20,10 +20,10 @@ import { SafeLink } from '../parser/types';
  * A collection of descriptive tags that can be added to
  * a clients description.
  */
-export enum Tag {
-    IOS = 'IOS',
-    ANDROID = 'ANDROID',
-    DESKTOP = 'DESKTOP',
+export enum Platform {
+    iOS = 'iOS',
+    Android = 'ANDROID',
+    Desktop = 'DESKTOP',
 }
 
 /*
@@ -45,6 +45,12 @@ export enum ClientKind {
     TEXT_CLIENT = 'TEXT_CLIENT',
 }
 
+export enum ClientId {
+    Element = 'element.io',
+    ElementDevelop = 'develop.element.io',
+    WeeChat = 'weechat',
+}
+
 /*
  * The descriptive details of a client
  */
@@ -54,8 +60,10 @@ export interface ClientDescription {
     homepage: string;
     logo: string;
     description: string;
-    tags: Tag[];
+    platform: Platform;
     maturity: Maturity;
+    clientId: ClientId;
+    experimental: boolean;
 }
 
 /*
@@ -72,7 +80,7 @@ export interface LinkedClient extends ClientDescription {
  */
 export interface TextClient extends ClientDescription {
     kind: ClientKind.TEXT_CLIENT;
-    toInviteString(parsedLink: SafeLink): string;
+    toInviteString(parsedLink: SafeLink): JSX.Element;
 }
 
 /*
diff --git a/src/contexts/ClientContext.ts b/src/contexts/ClientContext.ts
index cb530cec2570d61ba936f09f9eb3b022e510cb99..52da9f2b1feaf42fa1eca0542a8bcea0156d453d 100644
--- a/src/contexts/ClientContext.ts
+++ b/src/contexts/ClientContext.ts
@@ -15,54 +15,96 @@ limitations under the License.
 */
 
 import React from 'react';
+import { object, string, boolean, TypeOf } from 'zod';
 
-import { prefixFetch, Client, discoverServer } from 'matrix-cypher';
+import { ClientId } from '../clients/types';
+import { persistReducer } from '../utils/localStorage';
 
-type State = {
-    clientURL: string;
-    client: Client;
-}[];
+const STATE_SCHEMA = object({
+    clientId: string().nullable(),
+    showOnlyDeviceClients: boolean(),
+    rememberSelection: boolean(),
+    showExperimentalClients: boolean(),
+});
+
+type State = TypeOf<typeof STATE_SCHEMA>;
 
 // Actions are a discriminated union.
-export enum ActionTypes {
-    AddClient = 'ADD_CLIENT',
-    RemoveClient = 'REMOVE_CLIENT',
+export enum ActionType {
+    SetClient = 'SET_CLIENT',
+    ToggleRememberSelection = 'TOGGLE_REMEMBER_SELECTION',
+    ToggleShowOnlyDeviceClients = 'TOGGLE_SHOW_ONLY_DEVICE_CLIENTS',
+    ToggleShowExperimentalClients = 'TOGGLE_SHOW_EXPERIMENTAL_CLIENTS',
 }
 
-export interface AddClient {
-    action: ActionTypes.AddClient;
-    clientURL: string;
+interface SetClient {
+    action: ActionType.SetClient;
+    clientId: ClientId;
 }
 
-export interface RemoveClient {
-    action: ActionTypes.RemoveClient;
-    clientURL: string;
+interface ToggleRememberSelection {
+    action: ActionType.ToggleRememberSelection;
 }
 
-export type Action = AddClient | RemoveClient;
-
-export const INITIAL_STATE: State = [];
-export const reducer = async (state: State, action: Action): Promise<State> => {
-    switch (action.action) {
-        case ActionTypes.AddClient:
-            return state.filter((x) => x.clientURL !== action.clientURL);
-
-        case ActionTypes.RemoveClient:
-            if (!state.filter((x) => x.clientURL === action.clientURL)) {
-                const resolvedURL = await discoverServer(action.clientURL);
-                state.push({
-                    clientURL: resolvedURL,
-                    client: prefixFetch(resolvedURL),
-                });
-            }
-    }
-    return state;
+interface ToggleShowOnlyDeviceClients {
+    action: ActionType.ToggleShowOnlyDeviceClients;
+}
+
+interface ToggleShowExperimentalClients {
+    action: ActionType.ToggleShowExperimentalClients;
+}
+
+export type Action =
+    | SetClient
+    | ToggleRememberSelection
+    | ToggleShowOnlyDeviceClients
+    | ToggleShowExperimentalClients;
+
+const INITIAL_STATE: State = {
+    clientId: null,
+    rememberSelection: false,
+    showOnlyDeviceClients: true,
+    showExperimentalClients: false,
 };
 
-// The null is a hack to make the type checker happy
-// create context does not need an argument
-const { Provider, Consumer } = React.createContext<typeof reducer | null>(null);
+export const [initialState, reducer] = persistReducer(
+    'default-client',
+    INITIAL_STATE,
+    STATE_SCHEMA,
+    (state: State, action: Action): State => {
+        switch (action.action) {
+            case ActionType.SetClient:
+                return {
+                    ...state,
+                    clientId: action.clientId,
+                };
+            case ActionType.ToggleRememberSelection:
+                return {
+                    ...state,
+                    rememberSelection: !state.rememberSelection,
+                };
+            case ActionType.ToggleShowOnlyDeviceClients:
+                return {
+                    ...state,
+                    showOnlyDeviceClients: !state.showOnlyDeviceClients,
+                };
+            case ActionType.ToggleShowExperimentalClients:
+                return {
+                    ...state,
+                    showExperimentalClients: !state.showExperimentalClients,
+                };
+            default:
+                return state;
+        }
+    }
+);
+
+// The defualt reducer needs to be overwritten with the one above
+// after it's been put through react's useReducer
+export const ClientContext = React.createContext<
+    [State, React.Dispatch<Action>]
+>([initialState, (): void => {}]);
 
 // Quick rename to make importing easier
-export const ClientProvider = Provider;
-export const ClientConsumer = Consumer;
+export const ClientProvider = ClientContext.Provider;
+export const ClientConsumer = ClientContext.Consumer;
diff --git a/src/contexts/GlobalContext.tsx b/src/contexts/GlobalContext.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..20b440144c342e28b9a5f422c971d1c6a338ea08
--- /dev/null
+++ b/src/contexts/GlobalContext.tsx
@@ -0,0 +1,36 @@
+/*
+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, { useReducer } from 'react';
+import { UserAgentProvider } from '@quentin-sommer/react-useragent';
+
+import {
+    ClientProvider,
+    reducer as clientReducer,
+    initialState as clientInitialState,
+} from './ClientContext';
+
+interface IProps {
+    children: React.ReactNode;
+}
+
+export default ({ children }: IProps): JSX.Element => (
+    <UserAgentProvider ua={window.navigator.userAgent}>
+        <ClientProvider value={useReducer(clientReducer, clientInitialState)}>
+            {children}
+        </ClientProvider>
+    </UserAgentProvider>
+);
diff --git a/src/contexts/HSContext.ts b/src/contexts/HSContext.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e6afb95f45493e90c1ea57435dcf5552c3e9bda8
--- /dev/null
+++ b/src/contexts/HSContext.ts
@@ -0,0 +1,113 @@
+/*
+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 { string, object, union, literal, TypeOf } from 'zod';
+
+import { persistReducer } from '../utils/localStorage';
+
+//import { prefixFetch, Client, discoverServer } from 'matrix-cypher';
+
+enum HSOptions {
+    // The homeserver contact policy hasn't
+    // been set yet.
+    Unset = 'UNSET',
+    // Matrix.to should only contact a single provided homeserver
+    TrustedClientOnly = 'TRUSTED_CLIENT_ONLY',
+    // Matrix.to may contact any homeserver it requires
+    Any = 'ANY',
+    // Matrix.to may not contact any homeservers
+    None = 'NONE',
+}
+
+const STATE_SCHEMA = union([
+    object({
+        option: literal(HSOptions.Unset),
+    }),
+    object({
+        option: literal(HSOptions.None),
+    }),
+    object({
+        option: literal(HSOptions.Any),
+    }),
+    object({
+        option: literal(HSOptions.TrustedClientOnly),
+        hs: string(),
+    }),
+]);
+
+type State = TypeOf<typeof STATE_SCHEMA>;
+
+// TODO: rename actions to something with more meaning out of context
+export enum ActionTypes {
+    SetHS = 'SET_HS',
+    SetAny = 'SET_ANY',
+    SetNone = 'SET_NONE',
+}
+
+export interface SetHS {
+    action: ActionTypes.SetHS;
+    HSURL: string;
+}
+
+export interface SetAny {
+    action: ActionTypes.SetAny;
+}
+
+export interface SetNone {
+    action: ActionTypes.SetNone;
+}
+
+export type Action = SetHS | SetAny | SetNone;
+
+export const INITIAL_STATE: State = {
+    option: HSOptions.Unset,
+};
+
+export const [initialState, reducer] = persistReducer(
+    'home-server-options',
+    INITIAL_STATE,
+    STATE_SCHEMA,
+    (state: State, action: Action): State => {
+        switch (action.action) {
+            case ActionTypes.SetNone:
+                return {
+                    option: HSOptions.None,
+                };
+            case ActionTypes.SetAny:
+                return {
+                    option: HSOptions.Any,
+                };
+            case ActionTypes.SetHS:
+                return {
+                    option: HSOptions.TrustedClientOnly,
+                    hs: action.HSURL,
+                };
+            default:
+                return state;
+        }
+    }
+);
+
+// The defualt reducer needs to be overwritten with the one above
+// after it's been put through react's useReducer
+const { Provider, Consumer } = React.createContext<
+    [State, React.Dispatch<Action>]
+>([initialState, (): void => {}]);
+
+// Quick rename to make importing easier
+export const HSProvider = Provider;
+export const HSConsumer = Consumer;
diff --git a/src/utils/localStorage.ts b/src/utils/localStorage.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b93a2fa6e36e6822185341e515d97e7b1e5f6978
--- /dev/null
+++ b/src/utils/localStorage.ts
@@ -0,0 +1,59 @@
+/*
+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 {
+  Schema,
+} from 'zod';
+import React from 'react';
+
+/*
+ * Initialises local storage to initial value if
+ * a value matching the schema is not in storage.
+ */
+export function persistReducer<T, A>(
+  stateKey: string,
+  initialState: T,
+  schema: Schema<T>,
+  reducer: React.Reducer<T, A>,
+): [T, React.Reducer<T, A>] {
+  let currentState = initialState;
+  // Try to load state from local storage
+  const stateInStorage = localStorage.getItem(stateKey);
+  if (stateInStorage) {
+    try {
+       // Validate state type
+       const parsedState = JSON.parse(stateInStorage);
+       if (parsedState as T) {
+          currentState = schema.parse(parsedState);
+       }
+    } catch (e) {
+      // if invalid delete state
+      localStorage.setItem(stateKey, JSON.stringify(initialState));
+    }
+  } else {
+    localStorage.setItem(stateKey, JSON.stringify(initialState));
+  }
+
+  return [
+    currentState, 
+    (state: T, action: A) => {
+      // state passed to this reducer is the source of truth
+      const newState = reducer(state, action);
+      localStorage.setItem(stateKey, JSON.stringify(newState));
+      return newState;
+    },
+  ];
+}