diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index 08a74dfca382cd6e3c963d257b075734ed20cd72..0ef0801a4f377d87b00979ac223630b8b8ea094b 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -368,6 +368,7 @@ GUIComponent.defaultProps = {
     canSave: false,
     canSaveAsCopy: false,
     canShare: false,
+    onUpdateProjectTitle: () => {},
     stageSizeMode: STAGE_SIZE_MODES.large
 };
 
diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx
index e33868b96bb0ad76fbefdd993badcd603c7fe64b..f046710e0f17ad4f24304e613373b6f40a4da604 100644
--- a/src/components/menu-bar/menu-bar.jsx
+++ b/src/components/menu-bar/menu-bar.jsx
@@ -333,7 +333,7 @@ class MenuBar extends React.Component {
                                     )}
                                 </MenuSection>
                                 <MenuSection>
-                                    <SBFileUploader>
+                                    <SBFileUploader onUpdateProjectTitle={this.props.onUpdateProjectTitle}>
                                         {(renderFileInput, loadProject) => (
                                             <MenuItem
                                                 onClick={loadProject}
diff --git a/src/containers/sb-file-uploader.jsx b/src/containers/sb-file-uploader.jsx
index a5d8c99a70078a94be55d7570087a73c80bfe636..46be8172465a65d2542d9f2c957eccad7e10f4d7 100644
--- a/src/containers/sb-file-uploader.jsx
+++ b/src/containers/sb-file-uploader.jsx
@@ -6,7 +6,6 @@ import {defineMessages, injectIntl, intlShape} from 'react-intl';
 
 import analytics from '../lib/analytics';
 import log from '../lib/log';
-import {setProjectTitle} from '../reducers/project-title';
 import {LoadingStates, onLoadedProject, onProjectUploadStarted} from '../reducers/project-state';
 
 import {
@@ -42,12 +41,20 @@ class SBFileUploader extends React.Component {
     constructor (props) {
         super(props);
         bindAll(this, [
+            'getProjectTitleFromFilename',
             'renderFileInput',
             'setFileInput',
             'handleChange',
             'handleClick'
         ]);
     }
+    getProjectTitleFromFilename (fileInputFilename) {
+        if (!fileInputFilename) return '';
+        // only parse title from files like "filename.sb2" or "filename.sb3"
+        const matches = fileInputFilename.match(/^(.*)\.sb[23]$/);
+        if (!matches) return '';
+        return matches[1].substring(0, 100); // truncate project title to max 100 chars
+    }
     // called when user has finished selecting a file to upload
     handleChange (e) {
         // Remove the hash if any (without triggering a hash change event or a reload)
@@ -77,14 +84,8 @@ class SBFileUploader extends React.Component {
         if (thisFileInput.files) { // Don't attempt to load if no file was selected
             this.props.onLoadingStarted();
             reader.readAsArrayBuffer(thisFileInput.files[0]);
-            // extract the title from the file and set it as current project title
-            if (thisFileInput.files[0].name) {
-                const matches = thisFileInput.files[0].name.match(/^(.*)\.sb3$/);
-                if (matches) {
-                    const truncatedProjectTitle = matches[1].substring(0, 100);
-                    this.props.onSetProjectTitle(truncatedProjectTitle);
-                }
-            }
+            const uploadedProjectTitle = this.getProjectTitleFromFilename(thisFileInput.files[0].name);
+            this.props.onUpdateProjectTitle(uploadedProjectTitle);
         }
     }
     handleClick () {
@@ -116,7 +117,7 @@ SBFileUploader.propTypes = {
     loadingState: PropTypes.oneOf(LoadingStates),
     onLoadingFinished: PropTypes.func,
     onLoadingStarted: PropTypes.func,
-    onSetProjectTitle: PropTypes.func,
+    onUpdateProjectTitle: PropTypes.func,
     vm: PropTypes.shape({
         loadProject: PropTypes.func
     })
@@ -131,14 +132,19 @@ const mapDispatchToProps = dispatch => ({
         dispatch(onLoadedProject(loadingState));
         dispatch(closeLoadingProject());
     },
-    onSetProjectTitle: title => dispatch(setProjectTitle(title)),
     onLoadingStarted: () => {
         dispatch(openLoadingProject());
         dispatch(onProjectUploadStarted());
     }
 });
 
+// Allow incoming props to override redux-provided props. Used to mock in tests.
+const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(
+    {}, stateProps, dispatchProps, ownProps
+);
+
 export default connect(
     mapStateToProps,
-    mapDispatchToProps
+    mapDispatchToProps,
+    mergeProps
 )(injectIntl(SBFileUploader));
diff --git a/test/unit/containers/sb-file-uploader.test.jsx b/test/unit/containers/sb-file-uploader.test.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..856b8f058f88412db5c2371c9f734581c5eacd1f
--- /dev/null
+++ b/test/unit/containers/sb-file-uploader.test.jsx
@@ -0,0 +1,87 @@
+import React from 'react';
+
+import {shallowWithIntl} from '../../helpers/intl-helpers.jsx';
+import configureStore from 'redux-mock-store';
+import SBFileUploader from '../../../src/containers/sb-file-uploader';
+import {LoadingState} from '../../../src/reducers/project-state';
+
+jest.mock('react-ga'); // must mock this entire library, or lib/analytics causes error
+
+describe('SBFileUploader Container', () => {
+    const mockStore = configureStore();
+    let onLoadingFinished;
+    let onLoadingStarted;
+    let onUpdateProjectTitle;
+    let store;
+
+    // Wrap this in a function so it gets test specific states and can be reused.
+    const getContainer = function () {
+        return (
+            <SBFileUploader
+                onLoadingFinished={onLoadingFinished}
+                onLoadingStarted={onLoadingStarted}
+                onUpdateProjectTitle={onUpdateProjectTitle}
+            >
+                {(renderFileInput, loadProject) => (
+                    <div
+                        onClick={loadProject}
+                    />
+                )}
+            </SBFileUploader>
+        );
+    };
+
+    beforeEach(() => {
+        store = mockStore({
+            scratchGui: {
+                projectState: {
+                    loadingState: LoadingState.SHOWING_WITH_ID
+                },
+                vm: {}
+            }
+        });
+        onUpdateProjectTitle = jest.fn();
+        onLoadingFinished = jest.fn();
+        onLoadingStarted = jest.fn();
+    });
+
+    test('correctly sets title with .sb3 filename', () => {
+        const wrapper = shallowWithIntl(getContainer(), {context: {store}});
+        const instance = wrapper
+            .dive() // unwrap redux Connect(InjectIntl(SBFileUploader))
+            .dive() // unwrap InjectIntl(SBFileUploader)
+            .instance(); // SBFileUploader
+        const projectName = instance.getProjectTitleFromFilename('my project is great.sb3');
+        expect(projectName).toBe('my project is great');
+    });
+
+    test('correctly sets title with .sb2 filename', () => {
+        const wrapper = shallowWithIntl(getContainer(), {context: {store}});
+        const instance = wrapper
+            .dive() // unwrap redux Connect(InjectIntl(SBFileUploader))
+            .dive() // unwrap InjectIntl(SBFileUploader)
+            .instance(); // SBFileUploader
+        const projectName = instance.getProjectTitleFromFilename('my project is great.sb2');
+        expect(projectName).toBe('my project is great');
+    });
+
+    test('sets blank title with .sb filename', () => {
+        const wrapper = shallowWithIntl(getContainer(), {context: {store}});
+        const instance = wrapper
+            .dive() // unwrap redux Connect(InjectIntl(SBFileUploader))
+            .dive() // unwrap InjectIntl(SBFileUploader)
+            .instance(); // SBFileUploader
+        const projectName = instance.getProjectTitleFromFilename('my project is great.sb');
+        expect(projectName).toBe('');
+    });
+
+    test('sets blank title with filename with no extension', () => {
+        const wrapper = shallowWithIntl(getContainer(), {context: {store}});
+        const instance = wrapper
+            .dive() // unwrap redux Connect(InjectIntl(SBFileUploader))
+            .dive() // unwrap InjectIntl(SBFileUploader)
+            .instance(); // SBFileUploader
+        const projectName = instance.getProjectTitleFromFilename('my project is great');
+        expect(projectName).toBe('');
+    });
+});