diff --git a/resources/scripts/api/server/createServerDatabase.ts b/resources/scripts/api/server/createServerDatabase.ts
new file mode 100644
index 000000000..90103337c
--- /dev/null
+++ b/resources/scripts/api/server/createServerDatabase.ts
@@ -0,0 +1,15 @@
+import { rawDataToServerDatabase, ServerDatabase } from '@/api/server/getServerDatabases';
+import http from '@/api/http';
+
+export default (uuid: string, data: { connectionsFrom: string; databaseName: string }): Promise<ServerDatabase> => {
+    return new Promise((resolve, reject) => {
+        http.post(`/api/client/servers/${uuid}/databases`, {
+            database: data.databaseName,
+            remote: data.connectionsFrom,
+        }, {
+            params: { include: 'password' },
+        })
+            .then(response => resolve(rawDataToServerDatabase(response.data.attributes)))
+            .catch(reject);
+    });
+};
diff --git a/resources/scripts/api/server/deleteServerDatabase.ts b/resources/scripts/api/server/deleteServerDatabase.ts
new file mode 100644
index 000000000..23275bd36
--- /dev/null
+++ b/resources/scripts/api/server/deleteServerDatabase.ts
@@ -0,0 +1,9 @@
+import http from '@/api/http';
+
+export default (uuid: string, database: string): Promise<void> => {
+    return new Promise((resolve, reject) => {
+        http.delete(`/api/client/servers/${uuid}/databases/${database}`)
+            .then(() => resolve())
+            .catch(reject);
+    });
+};
diff --git a/resources/scripts/api/server/getServerDatabases.ts b/resources/scripts/api/server/getServerDatabases.ts
new file mode 100644
index 000000000..835964c27
--- /dev/null
+++ b/resources/scripts/api/server/getServerDatabases.ts
@@ -0,0 +1,31 @@
+import http from '@/api/http';
+
+export interface ServerDatabase {
+    id: string;
+    name: string;
+    username: string;
+    connectionString: string;
+    allowConnectionsFrom: string;
+    password?: string;
+}
+
+export const rawDataToServerDatabase = (data: any): ServerDatabase => ({
+    id: data.id,
+    name: data.name,
+    username: data.username,
+    connectionString: `${data.host.address}:${data.host.port}`,
+    allowConnectionsFrom: data.connections_from,
+    password: data.relationships && data.relationships.password ? data.relationships.password.attributes.password : undefined,
+});
+
+export default (uuid: string, includePassword: boolean = true): Promise<ServerDatabase[]> => {
+    return new Promise((resolve, reject) => {
+        http.get(`/api/client/servers/${uuid}/databases`, {
+            params: includePassword ? { include: 'password' } : undefined,
+        })
+            .then(response => resolve(
+                (response.data.data || []).map((item: any) => rawDataToServerDatabase(item.attributes))
+            ))
+            .catch(reject);
+    });
+};
diff --git a/resources/scripts/components/FlashMessageRender.tsx b/resources/scripts/components/FlashMessageRender.tsx
index 5b9c51d75..cb0b37026 100644
--- a/resources/scripts/components/FlashMessageRender.tsx
+++ b/resources/scripts/components/FlashMessageRender.tsx
@@ -1,16 +1,16 @@
 import React from 'react';
 import MessageBox from '@/components/MessageBox';
 import { State, useStoreState } from 'easy-peasy';
-import { ApplicationState } from '@/state/types';
+import { ApplicationStore } from '@/state';
 
 type Props = Readonly<{
     byKey?: string;
     spacerClass?: string;
-    withBottomSpace?: boolean;
+    className?: string;
 }>;
 
-export default ({ withBottomSpace, spacerClass, byKey }: Props) => {
-    const flashes = useStoreState((state: State<ApplicationState>) => state.flashes.items);
+export default ({ className, spacerClass, byKey }: Props) => {
+    const flashes = useStoreState((state: State<ApplicationStore>) => state.flashes.items);
 
     let filtered = flashes;
     if (byKey) {
@@ -21,9 +21,8 @@ export default ({ withBottomSpace, spacerClass, byKey }: Props) => {
         return null;
     }
 
-    // noinspection PointlessBooleanExpressionJS
     return (
-        <div className={withBottomSpace === false ? undefined : 'mb-2'}>
+        <div className={className}>
             {
                 filtered.map((flash, index) => (
                     <React.Fragment key={flash.id || flash.type + flash.message}>
diff --git a/resources/scripts/components/auth/ForgotPasswordContainer.tsx b/resources/scripts/components/auth/ForgotPasswordContainer.tsx
index fbf0eb704..668875ca0 100644
--- a/resources/scripts/components/auth/ForgotPasswordContainer.tsx
+++ b/resources/scripts/components/auth/ForgotPasswordContainer.tsx
@@ -4,14 +4,14 @@ import requestPasswordResetEmail from '@/api/auth/requestPasswordResetEmail';
 import { httpErrorToHuman } from '@/api/http';
 import LoginFormContainer from '@/components/auth/LoginFormContainer';
 import { Actions, useStoreActions } from 'easy-peasy';
-import { ApplicationState } from '@/state/types';
 import FlashMessageRender from '@/components/FlashMessageRender';
+import { ApplicationStore } from '@/state';
 
 export default () => {
     const [ isSubmitting, setSubmitting ] = React.useState(false);
     const [ email, setEmail ] = React.useState('');
 
-    const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationState>) => actions.flashes);
+    const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
 
     const handleFieldUpdate = (e: React.ChangeEvent<HTMLInputElement>) => setEmail(e.target.value);
 
diff --git a/resources/scripts/components/auth/LoginCheckpointContainer.tsx b/resources/scripts/components/auth/LoginCheckpointContainer.tsx
index ccc53e5bc..54ac3ee48 100644
--- a/resources/scripts/components/auth/LoginCheckpointContainer.tsx
+++ b/resources/scripts/components/auth/LoginCheckpointContainer.tsx
@@ -4,15 +4,15 @@ import loginCheckpoint from '@/api/auth/loginCheckpoint';
 import { httpErrorToHuman } from '@/api/http';
 import LoginFormContainer from '@/components/auth/LoginFormContainer';
 import { Actions, useStoreActions } from 'easy-peasy';
-import { ApplicationState } from '@/state/types';
 import { StaticContext } from 'react-router';
 import FlashMessageRender from '@/components/FlashMessageRender';
+import { ApplicationStore } from '@/state';
 
 export default ({ history, location: { state } }: RouteComponentProps<{}, StaticContext, { token?: string }>) => {
     const [ code, setCode ] = useState('');
     const [ isLoading, setIsLoading ] = useState(false);
 
-    const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationState>) => actions.flashes);
+    const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
 
     if (!state || !state.token) {
         history.replace('/auth/login');
diff --git a/resources/scripts/components/auth/LoginContainer.tsx b/resources/scripts/components/auth/LoginContainer.tsx
index f29a8c599..95538d78c 100644
--- a/resources/scripts/components/auth/LoginContainer.tsx
+++ b/resources/scripts/components/auth/LoginContainer.tsx
@@ -5,14 +5,14 @@ import { httpErrorToHuman } from '@/api/http';
 import LoginFormContainer from '@/components/auth/LoginFormContainer';
 import FlashMessageRender from '@/components/FlashMessageRender';
 import { Actions, useStoreActions } from 'easy-peasy';
-import { ApplicationState } from '@/state/types';
+import { ApplicationStore } from '@/state';
 
 export default ({ history }: RouteComponentProps) => {
     const [ username, setUsername ] = useState('');
     const [ password, setPassword ] = useState('');
     const [ isLoading, setLoading ] = useState(false);
 
-    const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationState>) => actions.flashes);
+    const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
 
     const submit = (e: React.FormEvent<HTMLFormElement>) => {
         e.preventDefault();
diff --git a/resources/scripts/components/auth/ResetPasswordContainer.tsx b/resources/scripts/components/auth/ResetPasswordContainer.tsx
index 9467bb92b..6954ebd2a 100644
--- a/resources/scripts/components/auth/ResetPasswordContainer.tsx
+++ b/resources/scripts/components/auth/ResetPasswordContainer.tsx
@@ -7,7 +7,7 @@ import { httpErrorToHuman } from '@/api/http';
 import LoginFormContainer from '@/components/auth/LoginFormContainer';
 import FlashMessageRender from '@/components/FlashMessageRender';
 import { Actions, useStoreActions } from 'easy-peasy';
-import { ApplicationState } from '@/state/types';
+import { ApplicationStore } from '@/state';
 
 type Props = Readonly<RouteComponentProps<{ token: string }> & {}>;
 
@@ -17,7 +17,7 @@ export default (props: Props) => {
     const [ password, setPassword ] = useState('');
     const [ passwordConfirm, setPasswordConfirm ] = useState('');
 
-    const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationState>) => actions.flashes);
+    const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
 
     const parsed = parse(props.location.search);
     if (email.length === 0 && parsed.email) {
diff --git a/resources/scripts/components/dashboard/DashboardContainer.tsx b/resources/scripts/components/dashboard/DashboardContainer.tsx
index 052d00034..a9c59a679 100644
--- a/resources/scripts/components/dashboard/DashboardContainer.tsx
+++ b/resources/scripts/components/dashboard/DashboardContainer.tsx
@@ -9,8 +9,8 @@ import { Link } from 'react-router-dom';
 
 export default () => (
     <div className={'my-10'}>
-        <Link to={'/server/e9d6c836'} className={'flex no-underline text-neutral-200 cursor-pointer items-center bg-neutral-700 p-4 border border-transparent hover:border-neutral-500'}>
-            <div className={'rounded-full bg-neutral-500 p-3'}>
+        <Link to={'/server/e9d6c836'} className={'grey-row-box cursor-pointer'}>
+            <div className={'icon'}>
                 <FontAwesomeIcon icon={faServer}/>
             </div>
             <div className={'w-1/2 ml-4'}>
@@ -49,8 +49,8 @@ export default () => (
                 </div>
             </div>
         </Link>
-        <div className={'flex mt-px cursor-pointer items-center bg-neutral-700 p-4 border border-transparent hover:border-neutral-500'}>
-            <div className={'rounded-full bg-neutral-500 p-3'}>
+        <div className={'grey-row-box cursor-pointer mt-2'}>
+            <div className={'icon'}>
                 <FontAwesomeIcon icon={faServer}/>
             </div>
             <div className={'w-1/2 ml-4'}>
diff --git a/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx b/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx
index 4593908a5..1ee88a9ca 100644
--- a/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx
+++ b/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx
@@ -1,11 +1,11 @@
 import React from 'react';
 import { Actions, State, useStoreActions, useStoreState } from 'easy-peasy';
-import { ApplicationState } from '@/state/types';
 import { Form, Formik, FormikActions } from 'formik';
 import * as Yup from 'yup';
 import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
 import Field from '@/components/elements/Field';
 import { httpErrorToHuman } from '@/api/http';
+import { ApplicationStore } from '@/state';
 
 interface Values {
     email: string;
@@ -18,10 +18,10 @@ const schema = Yup.object().shape({
 });
 
 export default () => {
-    const user = useStoreState((state: State<ApplicationState>) => state.user.data);
-    const updateEmail = useStoreActions((state: Actions<ApplicationState>) => state.user.updateUserEmail);
+    const user = useStoreState((state: State<ApplicationStore>) => state.user.data);
+    const updateEmail = useStoreActions((state: Actions<ApplicationStore>) => state.user.updateUserEmail);
 
-    const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationState>) => actions.flashes);
+    const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
 
     const submit = (values: Values, { resetForm, setSubmitting }: FormikActions<Values>) => {
         clearFlashes('account:email');
diff --git a/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx b/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx
index dce13e650..7c25c1287 100644
--- a/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx
+++ b/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx
@@ -1,12 +1,12 @@
 import React from 'react';
 import { Actions, State, useStoreActions, useStoreState } from 'easy-peasy';
-import { ApplicationState } from '@/state/types';
 import { Form, Formik, FormikActions } from 'formik';
 import Field from '@/components/elements/Field';
 import * as Yup from 'yup';
 import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
 import updateAccountPassword from '@/api/account/updateAccountPassword';
 import { httpErrorToHuman } from '@/api/http';
+import { ApplicationStore } from '@/state';
 
 interface Values {
     current: string;
@@ -23,8 +23,8 @@ const schema = Yup.object().shape({
 });
 
 export default () => {
-    const user = useStoreState((state: State<ApplicationState>) => state.user.data);
-    const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationState>) => actions.flashes);
+    const user = useStoreState((state: State<ApplicationStore>) => state.user.data);
+    const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
 
     if (!user) {
         return null;
diff --git a/resources/scripts/components/elements/Modal.tsx b/resources/scripts/components/elements/Modal.tsx
new file mode 100644
index 000000000..be1e62e20
--- /dev/null
+++ b/resources/scripts/components/elements/Modal.tsx
@@ -0,0 +1,71 @@
+import React, { useEffect, useState } from 'react';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faTimes } from '@fortawesome/free-solid-svg-icons/faTimes';
+import { CSSTransition } from 'react-transition-group';
+import Spinner from '@/components/elements/Spinner';
+
+interface Props {
+    visible: boolean;
+    onDismissed: () => void;
+    dismissable?: boolean;
+    closeOnEscape?: boolean;
+    closeOnBackground?: boolean;
+    showSpinnerOverlay?: boolean;
+    children: React.ReactNode;
+}
+
+export default (props: Props) => {
+    const [render, setRender] = useState(props.visible);
+
+    const handleEscapeEvent = (e: KeyboardEvent) => {
+        if (props.dismissable !== false && props.closeOnEscape !== false && e.key === 'Escape') {
+            setRender(false);
+        }
+    };
+
+    useEffect(() => setRender(props.visible), [props.visible]);
+
+    useEffect(() => {
+        window.addEventListener('keydown', handleEscapeEvent);
+
+        return () => window.removeEventListener('keydown', handleEscapeEvent);
+    }, [render]);
+
+    return (
+        <CSSTransition
+            timeout={250}
+            classNames={'fade'}
+            in={render}
+            unmountOnExit={true}
+            onExited={() => props.onDismissed()}
+        >
+            <div className={'modal-mask'} onClick={e => {
+                if (props.dismissable !== false && props.closeOnBackground !== false) {
+                    e.stopPropagation();
+                    if (e.target === e.currentTarget) {
+                        setRender(false);
+                    }
+                }
+            }}>
+                <div className={'modal-container top'}>
+                    {props.dismissable !== false &&
+                    <div className={'modal-close-icon'} onClick={() => setRender(false)}>
+                        <FontAwesomeIcon icon={faTimes}/>
+                    </div>
+                    }
+                    {props.showSpinnerOverlay &&
+                    <div
+                        className={'absolute w-full h-full rounded flex items-center justify-center'}
+                        style={{ background: 'hsla(211, 10%, 53%, 0.25)' }}
+                    >
+                        <Spinner large={false}/>
+                    </div>
+                    }
+                    <div className={'modal-content p-6'}>
+                        {props.children}
+                    </div>
+                </div>
+            </div>
+        </CSSTransition>
+    );
+};
diff --git a/resources/scripts/components/elements/Spinner.tsx b/resources/scripts/components/elements/Spinner.tsx
index 090980e3f..d1704916f 100644
--- a/resources/scripts/components/elements/Spinner.tsx
+++ b/resources/scripts/components/elements/Spinner.tsx
@@ -1,6 +1,11 @@
 import React from 'react';
 import classNames from 'classnames';
 
-export default ({ large }: { large?: boolean }) => (
-    <div className={classNames('spinner-circle spinner-white', { 'spinner-lg': large })}/>
+export default ({ large, centered }: { large?: boolean; centered?: boolean }) => (
+    centered ?
+        <div className={classNames('flex justify-center', { 'm-20': large, 'm-6': !large })}>
+            <div className={classNames('spinner-circle spinner-white', { 'spinner-lg': large })}/>
+        </div>
+        :
+        <div className={classNames('spinner-circle spinner-white', { 'spinner-lg': large })}/>
 );
diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx
index e21d4d07a..66f0b76bd 100644
--- a/resources/scripts/components/server/Console.tsx
+++ b/resources/scripts/components/server/Console.tsx
@@ -2,9 +2,9 @@ import React, { createRef } from 'react';
 import { Terminal } from 'xterm';
 import * as TerminalFit from 'xterm/lib/addons/fit/fit';
 import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
-import { ApplicationState } from '@/state/types';
 import { connect } from 'react-redux';
 import { Websocket } from '@/plugins/Websocket';
+import { ServerStore } from '@/state/server';
 
 const theme = {
     background: 'transparent',
@@ -113,8 +113,8 @@ class Console extends React.PureComponent<Readonly<Props>> {
 }
 
 export default connect(
-    (state: ApplicationState) => ({
-        connected: state.server.socket.connected,
-        instance: state.server.socket.instance,
+    (state: ServerStore) => ({
+        connected: state.socket.connected,
+        instance: state.socket.instance,
     }),
 )(Console);
diff --git a/resources/scripts/components/server/ServerConsole.tsx b/resources/scripts/components/server/ServerConsole.tsx
index 0264e0d64..19a8d9d1b 100644
--- a/resources/scripts/components/server/ServerConsole.tsx
+++ b/resources/scripts/components/server/ServerConsole.tsx
@@ -1,10 +1,9 @@
 import React from 'react';
 import Console from '@/components/server/Console';
-import { State, useStoreState } from 'easy-peasy';
-import { ApplicationState } from '@/state/types';
+import { ServerContext } from '@/state/server';
 
 export default () => {
-    const status = useStoreState((state: State<ApplicationState>) => state.server.status);
+    const status = ServerContext.useStoreState(state => state.status.value);
 
     return (
         <div className={'my-10 flex'}>
diff --git a/resources/scripts/components/server/WebsocketHandler.tsx b/resources/scripts/components/server/WebsocketHandler.tsx
index 7f74a64e8..a8d2f3003 100644
--- a/resources/scripts/components/server/WebsocketHandler.tsx
+++ b/resources/scripts/components/server/WebsocketHandler.tsx
@@ -1,13 +1,12 @@
 import React, { useEffect } from 'react';
-import { Actions, State, useStoreActions, useStoreState } from 'easy-peasy';
-import { ApplicationState } from '@/state/types';
 import { Websocket } from '@/plugins/Websocket';
+import { ServerContext } from '@/state/server';
 
 export default () => {
-    const server = useStoreState((state: State<ApplicationState>) => state.server.data);
-    const instance = useStoreState((state: State<ApplicationState>) => state.server.socket.instance);
-    const setServerStatus = useStoreActions((actions: Actions<ApplicationState>) => actions.server.setServerStatus);
-    const { setInstance, setConnectionState } = useStoreActions((actions: Actions<ApplicationState>) => actions.server.socket);
+    const server = ServerContext.useStoreState(state => state.server.data);
+    const instance = ServerContext.useStoreState(state => state.socket.instance);
+    const setServerStatus = ServerContext.useStoreActions(actions => actions.status.setServerStatus);
+    const { setInstance, setConnectionState } = ServerContext.useStoreActions(actions => actions.socket);
 
     useEffect(() => {
         // If there is already an instance or there is no server, just exit out of this process
@@ -20,7 +19,7 @@ export default () => {
 
         const socket = new Websocket(
             `wss://wings.pterodactyl.test:8080/api/servers/${server.uuid}/ws`,
-            'CC8kHCuMkXPosgzGO6d37wvhNcksWxG6kTrA'
+            'CC8kHCuMkXPosgzGO6d37wvhNcksWxG6kTrA',
         );
 
         socket.on('SOCKET_OPEN', () => setConnectionState(true));
diff --git a/resources/scripts/components/server/databases/CreateDatabaseButton.tsx b/resources/scripts/components/server/databases/CreateDatabaseButton.tsx
new file mode 100644
index 000000000..54261605d
--- /dev/null
+++ b/resources/scripts/components/server/databases/CreateDatabaseButton.tsx
@@ -0,0 +1,113 @@
+import React, { useState } from 'react';
+import { ServerDatabase } from '@/api/server/getServerDatabases';
+import Modal from '@/components/elements/Modal';
+import { Form, Formik, FormikActions } from 'formik';
+import Field from '@/components/elements/Field';
+import { object, string } from 'yup';
+import createServerDatabase from '@/api/server/createServerDatabase';
+import { ServerContext } from '@/state/server';
+import { Actions, useStoreActions } from 'easy-peasy';
+import { ApplicationStore } from '@/state';
+import { httpErrorToHuman } from '@/api/http';
+import FlashMessageRender from '@/components/FlashMessageRender';
+
+interface Values {
+    databaseName: string;
+    connectionsFrom: string;
+}
+
+const schema = object().shape({
+    databaseName: string()
+        .required('A database name must be provided.')
+        .min(5, 'Database name must be at least 5 characters.')
+        .max(64, 'Database name must not exceed 64 characters.')
+        .matches(/^[A-Za-z0-9_\-.]{5,64}$/, 'Database name should only contain alphanumeric characters, underscores, dashes, and/or periods.'),
+    connectionsFrom: string()
+        .required('A connection value must be provided.')
+        .matches(/^([1-9]{1,3}|%)(\.([0-9]{1,3}|%))?(\.([0-9]{1,3}|%))?(\.([0-9]{1,3}|%))?$/, 'A valid connection address must be provided.'),
+});
+
+export default ({ onCreated }: { onCreated: (database: ServerDatabase) => void }) => {
+    const [ visible, setVisible ] = useState(false);
+    const { addFlash, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
+    const server = ServerContext.useStoreState(state => state.server.data!);
+
+    const submit = (values: Values, { setSubmitting }: FormikActions<Values>) => {
+        clearFlashes();
+        createServerDatabase(server.uuid, { ...values })
+            .then(database => {
+                onCreated(database);
+                setVisible(false);
+            })
+            .catch(error => {
+                console.log(error);
+                addFlash({
+                    key: 'create-database-modal',
+                    type: 'error',
+                    title: 'Error',
+                    message: httpErrorToHuman(error),
+                });
+            })
+            .then(() => setSubmitting(false));
+    };
+
+    return (
+        <React.Fragment>
+            <Formik
+                onSubmit={submit}
+                initialValues={{ databaseName: '', connectionsFrom: '%' }}
+                validationSchema={schema}
+            >
+                {
+                    ({ isSubmitting, resetForm }) => (
+                        <Modal
+                            visible={visible}
+                            dismissable={!isSubmitting}
+                            showSpinnerOverlay={isSubmitting}
+                            onDismissed={() => {
+                                resetForm();
+                                setVisible(false);
+                            }}
+                        >
+                            <FlashMessageRender byKey={'create-database-modal'} className={'mb-6'}/>
+                            <h3 className={'mb-6'}>Create new database</h3>
+                            <Form className={'m-0'}>
+                                <Field
+                                    type={'string'}
+                                    id={'database_name'}
+                                    name={'databaseName'}
+                                    label={'Database Name'}
+                                    description={'A descriptive name for your database instance.'}
+                                />
+                                <div className={'mt-6'}>
+                                    <Field
+                                        type={'string'}
+                                        id={'connections_from'}
+                                        name={'connectionsFrom'}
+                                        label={'Connections From'}
+                                        description={'Where connections should be allowed from. Use % for wildcards.'}
+                                    />
+                                </div>
+                                <div className={'mt-6 text-right'}>
+                                    <button
+                                        type={'button'}
+                                        className={'btn btn-sm btn-secondary mr-2'}
+                                        onClick={() => setVisible(false)}
+                                    >
+                                        Cancel
+                                    </button>
+                                    <button className={'btn btn-sm btn-primary'} type={'submit'}>
+                                        Create Database
+                                    </button>
+                                </div>
+                            </Form>
+                        </Modal>
+                    )
+                }
+            </Formik>
+            <button className={'btn btn-primary btn-lg'} onClick={() => setVisible(true)}>
+                New Database
+            </button>
+        </React.Fragment>
+    );
+};
diff --git a/resources/scripts/components/server/databases/DatabaseRow.tsx b/resources/scripts/components/server/databases/DatabaseRow.tsx
new file mode 100644
index 000000000..3cad11755
--- /dev/null
+++ b/resources/scripts/components/server/databases/DatabaseRow.tsx
@@ -0,0 +1,159 @@
+import React, { useState } from 'react';
+import { ServerDatabase } from '@/api/server/getServerDatabases';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faDatabase } from '@fortawesome/free-solid-svg-icons/faDatabase';
+import { faTrashAlt } from '@fortawesome/free-solid-svg-icons/faTrashAlt';
+import { faEye } from '@fortawesome/free-solid-svg-icons/faEye';
+import classNames from 'classnames';
+import Modal from '@/components/elements/Modal';
+import { Form, Formik, FormikActions } from 'formik';
+import Field from '@/components/elements/Field';
+import { object, string } from 'yup';
+import FlashMessageRender from '@/components/FlashMessageRender';
+import { Actions, useStoreActions } from 'easy-peasy';
+import { ApplicationStore } from '@/state';
+import { ServerContext } from '@/state/server';
+import deleteServerDatabase from '@/api/server/deleteServerDatabase';
+import { httpErrorToHuman } from '@/api/http';
+
+interface Props {
+    database: ServerDatabase;
+    className?: string;
+    onDelete: () => void;
+}
+
+export default ({ database, className, onDelete }: Props) => {
+    const [visible, setVisible] = useState(false);
+    const [connectionVisible, setConnectionVisible] = useState(false);
+    const { addFlash, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
+    const server = ServerContext.useStoreState(state => state.server.data!);
+
+    const schema = object().shape({
+        confirm: string()
+            .required('The database name must be provided.')
+            .oneOf([database.name.split('_', 2)[1], database.name], 'The database name must be provided.'),
+    });
+
+    const submit = (values: { confirm: string }, { setSubmitting }: FormikActions<{ confirm: string }>) => {
+        clearFlashes();
+        deleteServerDatabase(server.uuid, database.id)
+            .then(() => {
+                setVisible(false);
+                setTimeout(() => onDelete(), 150);
+            })
+            .catch(error => {
+                console.error(error);
+                setSubmitting(false);
+                addFlash({
+                    key: 'delete-database-modal',
+                    type: 'error',
+                    title: 'Error',
+                    message: httpErrorToHuman(error),
+                });
+            });
+    };
+
+    return (
+        <React.Fragment>
+            <Formik
+                onSubmit={submit}
+                initialValues={{ confirm: '' }}
+                validationSchema={schema}
+            >
+                {
+                    ({ isSubmitting, isValid, resetForm }) => (
+                        <Modal
+                            visible={visible}
+                            dismissable={!isSubmitting}
+                            showSpinnerOverlay={isSubmitting}
+                            onDismissed={() => { setVisible(false); resetForm(); }}
+                        >
+                            <FlashMessageRender byKey={'delete-database-modal'} className={'mb-6'}/>
+                            <h3 className={'mb-6'}>Confirm database deletion</h3>
+                            <p className={'text-sm'}>
+                                Deleting a database is a permanent action, it cannot be undone. This will permanetly
+                                delete the <strong>{database.name}</strong> database and remove all associated data.
+                            </p>
+                            <Form className={'m-0 mt-6'}>
+                                <Field
+                                    type={'text'}
+                                    id={'confirm_name'}
+                                    name={'confirm'}
+                                    label={'Confirm Database Name'}
+                                    description={'Enter the database name to confirm deletion.'}
+                                />
+                                <div className={'mt-6 text-right'}>
+                                    <button
+                                        type={'button'}
+                                        className={'btn btn-sm btn-secondary mr-2'}
+                                        onClick={() => setVisible(false)}
+                                    >
+                                        Cancel
+                                    </button>
+                                    <button
+                                        type={'submit'}
+                                        className={'btn btn-sm btn-red'}
+                                        disabled={!isValid}
+                                    >
+                                        Delete Database
+                                    </button>
+                                </div>
+                            </Form>
+                        </Modal>
+                    )
+                }
+            </Formik>
+            <Modal visible={connectionVisible} onDismissed={() => setConnectionVisible(false)}>
+                <h3 className={'mb-6'}>Database connection details</h3>
+                <div>
+                    <label className={'input-dark-label'}>Password</label>
+                    <input type={'text'} className={'input-dark'} readOnly={true} value={database.password}/>
+                </div>
+                <div className={'mt-6'}>
+                    <label className={'input-dark-label'}>JBDC Connection String</label>
+                    <input
+                        type={'text'}
+                        className={'input-dark'}
+                        readOnly={true}
+                        value={`jdbc:mysql://${database.username}:${database.password}@${database.connectionString}/${database.name}`}
+                    />
+                </div>
+                <div className={'mt-6 text-right'}>
+                    <button className={'btn btn-sm btn-secondary'} onClick={() => setConnectionVisible(false)}>
+                        Close
+                    </button>
+                </div>
+            </Modal>
+            <div className={classNames('grey-row-box no-hover', className)}>
+                <div className={'icon'}>
+                    <FontAwesomeIcon icon={faDatabase}/>
+                </div>
+                <div className={'flex-1 ml-4'}>
+                    <p className={'text-lg'}>{database.name}</p>
+                </div>
+                <div className={'ml-6'}>
+                    <p className={'text-center text-xs text-neutral-500 uppercase mb-1 select-none'}>Endpoint:</p>
+                    <p className={'text-center text-sm'}>{database.connectionString}</p>
+                </div>
+                <div className={'ml-6'}>
+                    <p className={'text-center text-xs text-neutral-500 uppercase mb-1 select-none'}>
+                        Connections From:
+                    </p>
+                    <p className={'text-center text-sm'}>{database.allowConnectionsFrom}</p>
+                </div>
+                <div className={'ml-6'}>
+                    <p className={'text-center text-xs text-neutral-500 uppercase mb-1 select-none'}>Username:</p>
+                    <p className={'text-center text-sm'}>{database.username}</p>
+                </div>
+                <div className={'ml-6'}>
+                    <button className={'btn btn-sm btn-secondary mr-2'} onClick={() => setConnectionVisible(true)}>
+                        <FontAwesomeIcon icon={faEye} fixedWidth={true}/>
+                    </button>
+                    <button className={'btn btn-sm btn-secondary btn-red'} onClick={() => setVisible(true)}>
+                        <FontAwesomeIcon icon={faTrashAlt} fixedWidth={true}/>
+                    </button>
+                </div>
+            </div>
+        </React.Fragment>
+    );
+};
diff --git a/resources/scripts/components/server/databases/DatabasesContainer.tsx b/resources/scripts/components/server/databases/DatabasesContainer.tsx
new file mode 100644
index 000000000..a7c2c1d72
--- /dev/null
+++ b/resources/scripts/components/server/databases/DatabasesContainer.tsx
@@ -0,0 +1,64 @@
+import React, { useEffect, useState } from 'react';
+import getServerDatabases, { ServerDatabase } from '@/api/server/getServerDatabases';
+import { ServerContext } from '@/state/server';
+import { Actions, useStoreActions } from 'easy-peasy';
+import { ApplicationStore } from '@/state';
+import { httpErrorToHuman } from '@/api/http';
+import FlashMessageRender from '@/components/FlashMessageRender';
+import DatabaseRow from '@/components/server/databases/DatabaseRow';
+import Spinner from '@/components/elements/Spinner';
+import { CSSTransition } from 'react-transition-group';
+import CreateDatabaseButton from '@/components/server/databases/CreateDatabaseButton';
+
+export default () => {
+    const [ loading, setLoading ] = useState(true);
+    const [ databases, setDatabases ] = useState<ServerDatabase[]>([]);
+    const server = ServerContext.useStoreState(state => state.server.data!);
+    const { addFlash, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
+
+    useEffect(() => {
+        clearFlashes('databases');
+        getServerDatabases(server.uuid)
+            .then(databases => {
+                setDatabases(databases);
+                setLoading(false);
+            })
+            .catch(error => addFlash({
+                key: 'databases',
+                title: 'Error',
+                message: httpErrorToHuman(error),
+                type: 'error',
+            }));
+    }, []);
+
+    return (
+        <div className={'my-10 mb-6'}>
+            <FlashMessageRender byKey={'databases'}/>
+            {loading ?
+                <Spinner large={true} centered={true}/>
+                :
+                <CSSTransition classNames={'fade'} timeout={250}>
+                    <React.Fragment>
+                        {databases.length > 0 ?
+                            databases.map((database, index) => (
+                                <DatabaseRow
+                                    key={database.id}
+                                    database={database}
+                                    onDelete={() => setDatabases(s => [ ...s.filter(d => d.id !== database.id) ])}
+                                    className={index > 0 ? 'mt-1' : undefined}
+                                />
+                            ))
+                            :
+                            <p className={'text-center text-sm text-neutral-200'}>
+                                It looks like you have no databases. Click the button below to create one now.
+                            </p>
+                        }
+                        <div className={'mt-6 flex justify-end'}>
+                            <CreateDatabaseButton onCreated={database => setDatabases(s => [ ...s, database ])}/>
+                        </div>
+                    </React.Fragment>
+                </CSSTransition>
+            }
+        </div>
+    );
+};
diff --git a/resources/scripts/routers/ServerRouter.tsx b/resources/scripts/routers/ServerRouter.tsx
index 62f5ff8e0..4b01f0b63 100644
--- a/resources/scripts/routers/ServerRouter.tsx
+++ b/resources/scripts/routers/ServerRouter.tsx
@@ -3,14 +3,16 @@ import { NavLink, Route, RouteComponentProps, Switch } from 'react-router-dom';
 import NavigationBar from '@/components/NavigationBar';
 import ServerConsole from '@/components/server/ServerConsole';
 import TransitionRouter from '@/TransitionRouter';
-import { Actions, State, useStoreActions, useStoreState } from 'easy-peasy';
-import { ApplicationState } from '@/state/types';
 import Spinner from '@/components/elements/Spinner';
 import WebsocketHandler from '@/components/server/WebsocketHandler';
+import { ServerContext } from '@/state/server';
+import { Provider } from 'react-redux';
+import DatabasesContainer from '@/components/server/databases/DatabasesContainer';
 
-export default ({ match, location }: RouteComponentProps<{ id: string }>) => {
-    const server = useStoreState((state: State<ApplicationState>) => state.server.data);
-    const { clearServerState, getServer } = useStoreActions((actions: Actions<ApplicationState>) => actions.server);
+const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => {
+    const server = ServerContext.useStoreState(state => state.server.data);
+    const getServer = ServerContext.useStoreActions(actions => actions.server.getServer);
+    const clearServerState = ServerContext.useStoreActions(actions => actions.clearServerState);
 
     if (!server) {
         getServer(match.params.id);
@@ -31,22 +33,31 @@ export default ({ match, location }: RouteComponentProps<{ id: string }>) => {
                     </div>
                 </div>
             </div>
-            <TransitionRouter>
-                <div className={'w-full mx-auto'} style={{ maxWidth: '1200px' }}>
-                    {!server ?
-                        <div className={'flex justify-center m-20'}>
-                            <Spinner large={true}/>
-                        </div>
-                        :
-                        <React.Fragment>
-                            <WebsocketHandler/>
-                            <Switch location={location}>
-                                <Route path={`${match.path}`} component={ServerConsole} exact/>
-                            </Switch>
-                        </React.Fragment>
-                    }
-                </div>
-            </TransitionRouter>
+            <Provider store={ServerContext.useStore()}>
+                <TransitionRouter>
+                    <div className={'w-full mx-auto'} style={{ maxWidth: '1200px' }}>
+                        {!server ?
+                            <div className={'flex justify-center m-20'}>
+                                <Spinner large={true}/>
+                            </div>
+                            :
+                            <React.Fragment>
+                                <WebsocketHandler/>
+                                <Switch location={location}>
+                                    <Route path={`${match.path}`} component={ServerConsole} exact/>
+                                    <Route path={`${match.path}/databases`} component={DatabasesContainer}/>
+                                </Switch>
+                            </React.Fragment>
+                        }
+                    </div>
+                </TransitionRouter>
+            </Provider>
         </React.Fragment>
     );
 };
+
+export default (props: RouteComponentProps<any>) => (
+    <ServerContext.Provider>
+        <ServerRouter {...props}/>
+    </ServerContext.Provider>
+);
diff --git a/resources/scripts/state/flashes.ts b/resources/scripts/state/flashes.ts
new file mode 100644
index 000000000..666778a11
--- /dev/null
+++ b/resources/scripts/state/flashes.ts
@@ -0,0 +1,28 @@
+import { Action, action } from 'easy-peasy';
+import { FlashMessageType } from '@/components/MessageBox';
+
+export interface FlashStore {
+    items: FlashMessage[];
+    addFlash: Action<FlashStore, FlashMessage>;
+    clearFlashes: Action<FlashStore, string | void>;
+}
+
+export interface FlashMessage {
+    id?: string;
+    key?: string;
+    type: FlashMessageType;
+    title?: string;
+    message: string;
+}
+
+const flashes: FlashStore = {
+    items: [],
+    addFlash: action((state, payload) => {
+        state.items.push(payload);
+    }),
+    clearFlashes: action((state, payload) => {
+        state.items = payload ? state.items.filter(flashes => flashes.key !== payload) : [];
+    }),
+};
+
+export default flashes;
diff --git a/resources/scripts/state/index.ts b/resources/scripts/state/index.ts
index 5e19365f8..db3181aa5 100644
--- a/resources/scripts/state/index.ts
+++ b/resources/scripts/state/index.ts
@@ -1,13 +1,15 @@
 import { createStore } from 'easy-peasy';
-import { ApplicationState } from '@/state/types';
-import flashes from '@/state/models/flashes';
-import user from '@/state/models/user';
-import server from '@/state/models/server';
+import flashes, { FlashStore } from '@/state/flashes';
+import user, { UserStore } from '@/state/user';
 
-const state: ApplicationState = {
+export interface ApplicationStore {
+    flashes: FlashStore;
+    user: UserStore;
+}
+
+const state: ApplicationStore = {
     flashes,
     user,
-    server,
 };
 
 export const store = createStore(state);
diff --git a/resources/scripts/state/models/flashes.ts b/resources/scripts/state/models/flashes.ts
deleted file mode 100644
index 97c9f080b..000000000
--- a/resources/scripts/state/models/flashes.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { action } from 'easy-peasy';
-import { FlashState } from '@/state/types';
-
-const flashes: FlashState = {
-    items: [],
-    addFlash: action((state, payload) => {
-        state.items.push(payload);
-    }),
-    clearFlashes: action((state, payload) => {
-        state.items = payload ? state.items.filter(flashes => flashes.key !== payload) : [];
-    }),
-};
-
-export default flashes;
diff --git a/resources/scripts/state/models/server.ts b/resources/scripts/state/models/server.ts
deleted file mode 100644
index c7b7942dd..000000000
--- a/resources/scripts/state/models/server.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import getServer, { Server } from '@/api/server/getServer';
-import { action, Action, thunk, Thunk } from 'easy-peasy';
-import socket, { SocketState } from './socket';
-
-export type ServerStatus = 'offline' | 'starting' | 'stopping' | 'running';
-
-export interface ServerState {
-    data?: Server;
-    status: ServerStatus;
-    socket: SocketState;
-    getServer: Thunk<ServerState, string, {}, any, Promise<void>>;
-    setServer: Action<ServerState, Server>;
-    setServerStatus: Action<ServerState, ServerStatus>;
-    clearServerState: Action<ServerState>;
-}
-
-const server: ServerState = {
-    socket,
-    status: 'offline',
-    getServer: thunk(async (actions, payload) => {
-        const server = await getServer(payload);
-        actions.setServer(server);
-    }),
-    setServer: action((state, payload) => {
-        state.data = payload;
-    }),
-    setServerStatus: action((state, payload) => {
-        state.status = payload;
-    }),
-    clearServerState: action(state => {
-        state.data = undefined;
-
-        if (state.socket.instance) {
-            state.socket.instance.removeAllListeners();
-            state.socket.instance.close();
-        }
-
-        state.socket.instance = null;
-        state.socket.connected = false;
-    }),
-};
-
-export default server;
diff --git a/resources/scripts/state/server/index.ts b/resources/scripts/state/server/index.ts
new file mode 100644
index 000000000..c29e61bf8
--- /dev/null
+++ b/resources/scripts/state/server/index.ts
@@ -0,0 +1,57 @@
+import getServer, { Server } from '@/api/server/getServer';
+import { action, Action, createContextStore, thunk, Thunk } from 'easy-peasy';
+import socket, { SocketStore } from './socket';
+
+export type ServerStatus = 'offline' | 'starting' | 'stopping' | 'running';
+
+interface ServerDataStore {
+    data?: Server;
+    getServer: Thunk<ServerDataStore, string, {}, any, Promise<void>>;
+    setServer: Action<ServerDataStore, Server>;
+}
+
+const server: ServerDataStore = {
+    getServer: thunk(async (actions, payload) => {
+        const server = await getServer(payload);
+        actions.setServer(server);
+    }),
+    setServer: action((state, payload) => {
+        state.data = payload;
+    }),
+};
+
+interface ServerStatusStore {
+    value: ServerStatus;
+    setServerStatus: Action<ServerStatusStore, ServerStatus>;
+}
+
+const status: ServerStatusStore = {
+    value: 'offline',
+    setServerStatus: action((state, payload) => {
+        state.value = payload;
+    }),
+};
+
+export interface ServerStore {
+    server: ServerDataStore;
+    socket: SocketStore;
+    status: ServerStatusStore;
+    clearServerState: Action<ServerStore>;
+}
+
+export const ServerContext = createContextStore<ServerStore>({
+    server,
+    socket,
+    status,
+    clearServerState: action(state => {
+        state.server.data = undefined;
+
+        if (state.socket.instance) {
+            state.socket.instance.removeAllListeners();
+            state.socket.instance.close();
+        }
+
+        state.socket.instance = null;
+        state.socket.connected = false;
+    }),
+}, { name: 'ServerStore' });
diff --git a/resources/scripts/state/models/socket.ts b/resources/scripts/state/server/socket.ts
similarity index 70%
rename from resources/scripts/state/models/socket.ts
rename to resources/scripts/state/server/socket.ts
index 10922feda..e67910668 100644
--- a/resources/scripts/state/models/socket.ts
+++ b/resources/scripts/state/server/socket.ts
@@ -1,14 +1,14 @@
 import { Action, action } from 'easy-peasy';
 import { Websocket } from '@/plugins/Websocket';
 
-export interface SocketState {
+export interface SocketStore {
     instance: Websocket | null;
     connected: boolean;
-    setInstance: Action<SocketState, Websocket | null>;
-    setConnectionState: Action<SocketState, boolean>;
+    setInstance: Action<SocketStore, Websocket | null>;
+    setConnectionState: Action<SocketStore, boolean>;
 }
 
-const socket: SocketState = {
+const socket: SocketStore = {
     instance: null,
     connected: false,
     setInstance: action((state, payload) => {
diff --git a/resources/scripts/state/types.d.ts b/resources/scripts/state/types.d.ts
deleted file mode 100644
index d54afc29d..000000000
--- a/resources/scripts/state/types.d.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { FlashMessageType } from '@/components/MessageBox';
-import { Action } from 'easy-peasy';
-import { UserState } from '@/state/models/user';
-import { ServerState } from '@/state/models/server';
-
-export interface ApplicationState {
-    flashes: FlashState;
-    user: UserState;
-    server: ServerState;
-}
-
-export interface FlashState {
-    items: FlashMessage[];
-    addFlash: Action<FlashState, FlashMessage>;
-    clearFlashes: Action<FlashState, string | void>;
-}
-
-export interface FlashMessage {
-    id?: string;
-    key?: string;
-    type: FlashMessageType;
-    title?: string;
-    message: string;
-}
diff --git a/resources/scripts/state/models/user.ts b/resources/scripts/state/user.ts
similarity index 80%
rename from resources/scripts/state/models/user.ts
rename to resources/scripts/state/user.ts
index c7da19fbc..087bfe003 100644
--- a/resources/scripts/state/models/user.ts
+++ b/resources/scripts/state/user.ts
@@ -12,14 +12,14 @@ export interface UserData {
     updatedAt: Date;
 }
 
-export interface UserState {
+export interface UserStore {
     data?: UserData;
-    setUserData: Action<UserState, UserData>;
-    updateUserData: Action<UserState, Partial<UserData>>;
-    updateUserEmail: Thunk<UserState, { email: string; password: string }, any, {}, Promise<void>>;
+    setUserData: Action<UserStore, UserData>;
+    updateUserData: Action<UserStore, Partial<UserData>>;
+    updateUserEmail: Thunk<UserStore, { email: string; password: string }, any, {}, Promise<void>>;
 }
 
-const user: UserState = {
+const user: UserStore = {
     data: undefined,
     setUserData: action((state, payload) => {
         state.data = payload;
diff --git a/resources/styles/components/miscellaneous.css b/resources/styles/components/miscellaneous.css
index 158970f9b..f023a834b 100644
--- a/resources/styles/components/miscellaneous.css
+++ b/resources/styles/components/miscellaneous.css
@@ -7,114 +7,15 @@ code.clean {
     display: inline-block;
 }
 
-/**
- * Indicators for server online status.
- */
-.indicator {
-    @apply .bg-neutral-800 .border .border-primary-500;
-    border-radius: 50%;
-    width: 16px;
-    height: 16px;
+.grey-row-box {
+    @apply .flex .rounded .no-underline .text-neutral-200 .items-center .bg-neutral-700 .p-4 .border .border-transparent;
+    transition: border-color 150ms linear;
 
-    &.online {
-        @apply .bg-green-600 .border-green-500;
-        animation: onlineblink 2s infinite alternate;
+    &:not(.no-hover):hover {
+        @apply .border-neutral-500;
     }
 
-    &.offline {
-        @apply .bg-green-600 .border-red-500;
-        animation: offlineblink 2s infinite alternate;
-    }
-}
-
-/**
- * Usage indicator labels for the server listing.
- */
-.usage {
-    @apply .flex-1 .text-center .relative;
-
-    & > .indicator-title {
-        @apply .text-xs .uppercase .font-hairline .bg-white .absolute .text-primary-500;
-        margin-top:-9px;
-        padding: 0 8px;
-        left: 50%;
-        transform: translate(-50%, 0);
-    }
-}
-
-/**
- * Styling for elements that contain the core page content.
- */
-.content-box {
-    @apply .bg-white .p-6 .rounded .shadow .border .border-neutral-100;
-}
-
-/**
- * Flex boxes for server listing on user dashboard.
- */
-.server-card-container {
-    @apply .mb-4 .w-full;
-
-    @screen md {
-        @apply .w-1/2 .pr-4;
-
-        &:nth-of-type(2n) {
-            @apply .pr-0;
-        }
-    }
-
-    @screen lg {
-        @apply .w-1/3 .pr-4;
-
-        &:nth-of-type(2n) {
-            @apply .pr-4;
-        }
-
-        &:nth-of-type(3n) {
-            @apply .pr-0;
-        }
-    }
-
-    & > div {
-        @apply .flex .flex-col;
-        transition: box-shadow 150ms ease-in;
-
-        &:hover {
-            @apply .shadow-md;
-        }
-    }
-
-    & > div > .server-card {
-        @apply .flex .flex-col .p-4 .border .border-t-4 .border-neutral-100 .bg-white;
-        transition: all 100ms ease-in;
-
-        & .identifier-icon {
-            @apply .select-none .inline-block .rounded-full .text-white .text-center .leading-none .justify-center .w-8 .h-8 .mr-2 .flex .flex-row .items-center;
-        }
-
-        & a, & a:visited {
-            @apply .no-underline .text-neutral-800;
-        }
-    }
-
-    & > div > .footer {
-        @apply .border .border-neutral-100 .border-t-0 .bg-neutral-50 .shadow-inner;
-    }
-}
-
-.pillbox {
-    @apply .rounded-full .px-2 .py-1 .text-white .font-medium .leading-none .text-xs;
-}
-
-.server-search {
-    @apply .w-full .my-4;
-
-    & > input[type="text"] {
-        @apply .w-full .p-4 .rounded .border .border-neutral-200 .text-neutral-500;
-        transition: border 150ms ease-in;
-
-        &:focus {
-            @apply .border-primary-500;
-        }
+    & > div.icon {
+        @apply .rounded-full .bg-neutral-500 .p-3;
     }
 }
diff --git a/resources/styles/components/modal.css b/resources/styles/components/modal.css
index 4deb58cbf..7c3a6ae94 100644
--- a/resources/styles/components/modal.css
+++ b/resources/styles/components/modal.css
@@ -1,19 +1,19 @@
 .modal-mask {
   @apply .fixed .pin .z-50 .overflow-auto .flex;
-  background: rgba(0, 0, 0, 0.7);
+  background: rgba(0, 0, 0, 0.70);
   transition: opacity 250ms ease;
 
   & > .modal-container {
     @apply .relative .w-full .max-w-md .m-auto .flex-col .flex;
 
     &.top {
-      margin-top: 15%;
+      margin-top: 10%;
     }
 
     & > .modal-close-icon {
       @apply .absolute .pin-r .p-2 .text-white .cursor-pointer .opacity-50;
       transition: opacity 150ms linear, transform 150ms ease-in;
-      top: -2.5rem;
+      top: -2rem;
 
       &:hover {
         @apply .opacity-100;
@@ -22,7 +22,7 @@
     }
 
     & > .modal-content {
-      @apply .bg-white .rounded .shadow-md;
+      @apply .bg-neutral-800 .rounded .shadow-md;
       transition: all 250ms ease;
     }
 
diff --git a/resources/styles/components/typography.css b/resources/styles/components/typography.css
index 301eabb2a..c6b3f10ff 100644
--- a/resources/styles/components/typography.css
+++ b/resources/styles/components/typography.css
@@ -13,5 +13,5 @@ h1, h2, h3, h4, h5, h6 {
 }
 
 p {
-    @apply .text-neutral-200;
+    @apply .text-neutral-200 .leading-snug;
 }
diff --git a/tailwind.js b/tailwind.js
index ec7f6c291..2f3314e52 100644
--- a/tailwind.js
+++ b/tailwind.js
@@ -290,7 +290,9 @@ module.exports = {
     leading: {
         'none': 1,
         'tight': 1.25,
+        'snug': 1.375,
         'normal': 1.5,
+        'relaxed': 1.625,
         'loose': 2,
     },
 
diff --git a/webpack.config.js b/webpack.config.js
index 0054d91d3..10bdb1c22 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -135,7 +135,7 @@ module.exports = {
     },
     plugins: plugins,
     optimization: {
-        minimize: true,
+        minimize: isProduction,
         minimizer: [
             new TerserPlugin({
                 cache: true,