diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index ea705596f58c60d7a9a4246a1a2e6e929d3ebf4f..49b29672da7f6a9c459520bd62fecce3fe8d7d91 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -20,6 +20,7 @@ import {
     closeBackdropLibrary
 } from '../reducers/modals';
 
+import FontLoaderHOC from '../lib/font-loader-hoc.jsx';
 import ProjectFetcherHOC from '../lib/project-fetcher-hoc.jsx';
 import ProjectSaverHOC from '../lib/project-saver-hoc.jsx';
 import vmListenerHOC from '../lib/vm-listener-hoc.jsx';
@@ -132,6 +133,7 @@ const ConnectedGUI = connect(
 // ability to compose reducers.
 const WrappedGui = compose(
     ErrorBoundaryHOC('Top Level App'),
+    FontLoaderHOC,
     ProjectFetcherHOC,
     ProjectSaverHOC,
     vmListenerHOC,
diff --git a/src/lib/font-loader-hoc.jsx b/src/lib/font-loader-hoc.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..cbb7856c31fb3c95c3e8990d41e2cbd21922330f
--- /dev/null
+++ b/src/lib/font-loader-hoc.jsx
@@ -0,0 +1,61 @@
+import React from 'react';
+
+/* Higher Order Component to provide behavior for loading fonts.
+ * @param {React.Component} WrappedComponent component to receive fontsLoaded prop
+ * @returns {React.Component} component with font loading behavior
+ */
+const FontLoaderHOC = function (WrappedComponent) {
+    class FontLoaderComponent extends React.Component {
+        constructor (props) {
+            super(props);
+            this.state = {
+                fontsLoaded: false
+            };
+        }
+        componentDidMount () {
+            const getFontPromises = () => {
+                const fontPromises = [];
+                // Browsers that support the font loader interface have an iterable document.fonts.values()
+                // Firefox has a mocked out object that doesn't actually implement iterable, which is why
+                // the deep safety check is necessary.
+                if (document.fonts &&
+                    typeof document.fonts.values === 'function' &&
+                    typeof document.fonts.values()[Symbol.iterator] === 'function') {
+                    for (const fontFace of document.fonts.values()) {
+                        fontPromises.push(fontFace.loaded);
+                        fontFace.load();
+                    }
+                }
+                return fontPromises;
+            };
+            // Font promises must be gathered after the document is loaded, because on Mac Chrome, the promise
+            // objects get replaced and the old ones never resolve.
+            if (document.readyState === 'complete') {
+                Promise.all(getFontPromises()).then(() => {
+                    this.setState({fontsLoaded: true});
+                });
+            } else {
+                document.onreadystatechange = () => {
+                    if (document.readyState !== 'complete') return;
+                    document.onreadystatechange = null;
+                    Promise.all(getFontPromises()).then(() => {
+                        this.setState({fontsLoaded: true});
+                    });
+                };
+            }
+        }
+        render () {
+            return (
+                <WrappedComponent
+                    fontsLoaded={this.state.fontsLoaded}
+                    {...this.props}
+                />
+            );
+        }
+    }
+    return FontLoaderComponent;
+};
+
+export {
+    FontLoaderHOC as default
+};
diff --git a/src/lib/vm-manager-hoc.jsx b/src/lib/vm-manager-hoc.jsx
index 449bff0eec2399b6792bda68b2b65c313a69e083..56bd7eabf74ddee9a6f0f714b9b12a25a45ef2f0 100644
--- a/src/lib/vm-manager-hoc.jsx
+++ b/src/lib/vm-manager-hoc.jsx
@@ -38,7 +38,10 @@ const vmManagerHOC = function (WrappedComponent) {
             this.props.vm.initialized = true;
         }
         componentDidUpdate (prevProps) {
-            if (this.props.isLoadingWithId && !prevProps.isLoadingWithId) {
+            // if project is in loading state, AND fonts are loaded,
+            // and they weren't both that way until now... load project!
+            if (this.props.isLoadingWithId && this.props.fontsLoaded &&
+                (!prevProps.isLoadingWithId || !prevProps.fontsLoaded)) {
                 this.loadProject(this.props.projectData, this.props.loadingState);
             }
         }
@@ -56,6 +59,7 @@ const vmManagerHOC = function (WrappedComponent) {
         render () {
             const {
                 /* eslint-disable no-unused-vars */
+                fontsLoaded,
                 onLoadedProject: onLoadedProjectProp,
                 projectData,
                 projectId,
@@ -65,10 +69,6 @@ const vmManagerHOC = function (WrappedComponent) {
                 vm,
                 ...componentProps
             } = this.props;
-            // don't display anything until we have data loaded
-            if (!this.props.projectData) {
-                return null;
-            }
             return (
                 <WrappedComponent
                     errorMessage={this.state.errorMessage}
@@ -82,6 +82,7 @@ const vmManagerHOC = function (WrappedComponent) {
     }
 
     VMManager.propTypes = {
+        fontsLoaded: PropTypes.bool,
         isLoadingWithId: PropTypes.bool,
         loadingState: PropTypes.oneOf(LoadingStates),
         onLoadedProject: PropTypes.func,
diff --git a/test/unit/util/vm-manager-hoc.test.jsx b/test/unit/util/vm-manager-hoc.test.jsx
index 0bcb8f4b8ea8a1d224af62494a95dd3f9f4720b0..39015a4ded01098f16cbb86d8ca80d568bf1e9b1 100644
--- a/test/unit/util/vm-manager-hoc.test.jsx
+++ b/test/unit/util/vm-manager-hoc.test.jsx
@@ -60,6 +60,7 @@ describe('VMManagerHOC', () => {
         const WrappedComponent = vmManagerHOC(Component);
         const mounted = mount(
             <WrappedComponent
+                fontsLoaded
                 isLoadingWithId={false}
                 store={store}
                 vm={vm}
@@ -75,27 +76,46 @@ describe('VMManagerHOC', () => {
         // nextTick needed since vm.loadProject is async, and we have to wait for it :/
         process.nextTick(() => expect(mockedOnLoadedProject).toHaveBeenLastCalledWith(LoadingState.LOADING_VM_WITH_ID));
     });
-    test('if there is projectData, the child is rendered', () => {
+    test('if the fontsLoaded prop becomes true, it loads project data into the vm', () => {
+        vm.loadProject = jest.fn(() => Promise.resolve());
+        const mockedOnLoadedProject = jest.fn();
         const Component = () => <div />;
         const WrappedComponent = vmManagerHOC(Component);
         const mounted = mount(
             <WrappedComponent
-                projectData="100"
+                isLoadingWithId
                 store={store}
                 vm={vm}
+                onLoadedProject={mockedOnLoadedProject}
             />
         );
-        expect(mounted.find('div').length).toBe(1);
+        mounted.setProps({
+            fontsLoaded: true,
+            loadingState: LoadingState.LOADING_VM_WITH_ID,
+            projectData: '100'
+        });
+        expect(vm.loadProject).toHaveBeenLastCalledWith('100');
+        // nextTick needed since vm.loadProject is async, and we have to wait for it :/
+        process.nextTick(() => expect(mockedOnLoadedProject).toHaveBeenLastCalledWith(LoadingState.LOADING_VM_WITH_ID));
     });
-    test('if there is no projectData, nothing is rendered', () => {
+    test('if the fontsLoaded prop is false, project data is never loaded', () => {
+        vm.loadProject = jest.fn(() => Promise.resolve());
+        const mockedOnLoadedProject = jest.fn();
         const Component = () => <div />;
         const WrappedComponent = vmManagerHOC(Component);
         const mounted = mount(
             <WrappedComponent
+                isLoadingWithId
                 store={store}
                 vm={vm}
+                onLoadedProject={mockedOnLoadedProject}
             />
         );
-        expect(mounted.find('div').length).toBe(0);
+        mounted.setProps({
+            loadingState: LoadingState.LOADING_VM_WITH_ID,
+            projectData: '100'
+        });
+        expect(vm.loadProject).toHaveBeenCalledTimes(0);
+        process.nextTick(() => expect(mockedOnLoadedProject).toHaveBeenCalledTimes(0));
     });
 });