diff --git a/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6
index 266f06c3e..be6834333 100644
--- a/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6
+++ b/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6
@@ -1,3 +1,5 @@
+import showModal from 'discourse/lib/show-modal';
+
 /**
   This controller supports interface for creating custom CSS skins in Discourse.
 
@@ -21,6 +23,10 @@ export default Ember.ArrayController.extend({
       this.set('selectedItem', item);
     },
 
+    importModal: function() {
+      showModal('upload-customization');
+    },
+
     /**
       Select a given style
 
diff --git a/app/assets/javascripts/admin/models/site_customization.js b/app/assets/javascripts/admin/models/site_customization.js
index 27b6ddbdd..f7b6346a9 100644
--- a/app/assets/javascripts/admin/models/site_customization.js
+++ b/app/assets/javascripts/admin/models/site_customization.js
@@ -78,13 +78,18 @@ Discourse.SiteCustomization = Discourse.Model.extend({
       siteCustomization.set('savingStatus', I18n.t('saved'));
       siteCustomization.set('saving',false);
       siteCustomization.startTrackingChanges();
+      return siteCustomization;
     });
   },
 
   destroy: function() {
     if (!this.id) return;
     return Discourse.ajax("/admin/site_customizations/" + this.id, { type: 'DELETE' });
-  }
+  },
+
+  download_url: function() {
+    return Discourse.getURL('/admin/site_customizations/' + this.id);
+  }.property('id')
 });
 
 var SiteCustomizations = Ember.ArrayProxy.extend({
diff --git a/app/assets/javascripts/admin/templates/customize_css_html.hbs b/app/assets/javascripts/admin/templates/customize_css_html.hbs
index 004318e1e..e2cc7951a 100644
--- a/app/assets/javascripts/admin/templates/customize_css_html.hbs
+++ b/app/assets/javascripts/admin/templates/customize_css_html.hbs
@@ -8,12 +8,14 @@
   <button {{action "newCustomization"}} class='btn'>
     {{fa-icon "plus"}}{{i18n 'admin.customize.new'}}
   </button>
+  {{d-button action="importModal" icon="upload" label="admin.customize.import"}}
 </div>
 
 {{#if selectedItem}}
   <div {{bind-attr class=":current-style view.maximized:maximized"}}>
     <div class='wrapper'>
       {{text-field class="style-name" value=selectedItem.name}}
+      <a class="btn export" download target="_blank" href={{selectedItem.download_url}}>{{fa-icon "download"}} {{i18n 'admin.export_json.button_text'}}</a>
 
       <div class='admin-controls'>
         <ul class="nav nav-pills">
diff --git a/app/assets/javascripts/discourse/components/json-file-uploader.js.es6 b/app/assets/javascripts/discourse/components/json-file-uploader.js.es6
new file mode 100644
index 000000000..c86784ca2
--- /dev/null
+++ b/app/assets/javascripts/discourse/components/json-file-uploader.js.es6
@@ -0,0 +1,105 @@
+
+export default Em.Component.extend({
+  fileInput: null,
+  loading: false,
+  expectedRootObjectName: null,
+  hover: 0,
+
+  classNames: ['json-uploader'],
+
+  _initialize: function() {
+    const $this = this.$();
+    const self = this;
+
+    const $fileInput = $this.find('#js-file-input');
+    this.set('fileInput', $fileInput[0]);
+
+    $fileInput.on('change', function() {
+      self.fileSelected(this.files);
+    });
+
+    const $dragContainer = $this.find('.jsfu-shade-container');
+
+    $this.on('dragover', function(e) {
+      if (e.preventDefault) e.preventDefault();
+      return false;
+    });
+    $this.on('dragenter', function(e) {
+      if (e.preventDefault) e.preventDefault();
+      self.set('hover', self.get('hover') + 1);
+      return false;
+    });
+    $this.on('dragleave', function(e) {
+      if (e.preventDefault) e.preventDefault();
+      self.set('hover', self.get('hover') - 1);
+      return false;
+    });
+    $this.on('drop', function(e) {
+      if (e.preventDefault) e.preventDefault();
+
+      self.set('hover', 0);
+      self.fileSelected(e.dataTransfer.files);
+      return false;
+    });
+
+  }.on('didInsertElement'),
+
+  accept: function() {
+    return ".json,application/json,application/x-javascript,text/json" + (this.get('extension') ? "," + this.get('extension') : "");
+  }.property('extension'),
+
+  setReady: function() {
+    let parsed;
+    try {
+      parsed = JSON.parse(this.get('value'));
+    } catch (e) {
+      this.set('ready', false);
+      return;
+    }
+
+    const rootObject = parsed[this.get('expectedRootObjectName')];
+
+    if (rootObject !== null && rootObject !== undefined) {
+      this.set('ready', true);
+    } else {
+      this.set('ready', false);
+    }
+  }.observes('destination', 'expectedRootObjectName'),
+
+  actions: {
+    selectFile: function() {
+      const $fileInput = $(this.get('fileInput'));
+      $fileInput.click();
+    }
+  },
+
+  fileSelected(fileList) {
+    const self = this;
+    let files = [];
+    for (let i = 0; i < fileList.length; i++) {
+      files[i] = fileList[i];
+    }
+    const fileNameRegex = /\.(json|txt)$/;
+    files = files.filter(function(file) {
+      if (fileNameRegex.test(file.name)) {
+        return true;
+      }
+      if (file.type === "text/plain") {
+        return true;
+      }
+      return false;
+    });
+    const firstFile = fileList[0];
+
+    this.set('loading', true);
+
+    let reader = new FileReader();
+    reader.onload = function(evt) {
+      self.set('value', evt.target.result);
+      self.set('loading', false);
+    };
+
+    reader.readAsText(firstFile);
+  }
+
+});
diff --git a/app/assets/javascripts/discourse/controllers/upload-customization.js.es6 b/app/assets/javascripts/discourse/controllers/upload-customization.js.es6
new file mode 100644
index 000000000..09158f537
--- /dev/null
+++ b/app/assets/javascripts/discourse/controllers/upload-customization.js.es6
@@ -0,0 +1,52 @@
+import ModalFunctionality from 'discourse/mixins/modal-functionality';
+
+export default Ember.Controller.extend(ModalFunctionality, {
+  notReady: Em.computed.not('ready'),
+
+  needs: ['admin-customize-css-html'],
+
+  title: "hi",
+
+  ready: function() {
+    let parsed;
+    try {
+      parsed = JSON.parse(this.get('customizationFile'));
+    } catch (e) {
+      return false;
+    }
+
+    return !!parsed["site_customization"];
+  }.property('customizationFile'),
+
+  actions: {
+    createCustomization: function() {
+      const self = this;
+      const object = JSON.parse(this.get('customizationFile')).site_customization;
+
+      // Slight fixup before creating object
+      object.enabled = false;
+      delete object.id;
+      delete object.key;
+
+      const customization = Discourse.SiteCustomization.create(object);
+
+      this.set('loading', true);
+      customization.save().then(function(customization) {
+        self.send('closeModal');
+        self.set('loading', false);
+
+        const parentController = self.get('controllers.admin-customize-css-html');
+        parentController.pushObject(customization);
+        parentController.set('selectedItem', customization);
+      }).catch(function(xhr) {
+        self.set('loading', false);
+        if (xhr.responseJSON) {
+          bootbox.alert(xhr.responseJSON.errors.join("<br>"));
+        } else {
+          bootbox.alert(I18n.t('generic_error'));
+        }
+      });
+    }
+  }
+
+});
diff --git a/app/assets/javascripts/discourse/templates/components/json-file-uploader.hbs b/app/assets/javascripts/discourse/templates/components/json-file-uploader.hbs
new file mode 100644
index 000000000..b34f6c4b9
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/components/json-file-uploader.hbs
@@ -0,0 +1,12 @@
+<div class="jsfu-shade-container">
+  <div class="jsfu-file">
+    <input id="js-file-input" type="file" style="display:none;" accept={{accept}}>
+    {{d-button class="fileSelect" action="selectFile" class="" icon="upload" label="upload_selector.select_file"}}
+    {{conditional-loading-spinner condition=loading size="small"}}
+  </div>
+  <div class="jsfu-separator">{{i18n "alternation"}}</div>
+  <div class="jsfu-paste">
+    {{textarea value=value}}
+  </div>
+  <div class="jsfu-shade {{if hover '' 'hidden'}}"><span class="text">{{fa-icon "upload"}}</span></div>
+</div>
diff --git a/app/assets/javascripts/discourse/templates/modal/upload-customization.hbs b/app/assets/javascripts/discourse/templates/modal/upload-customization.hbs
new file mode 100644
index 000000000..6d5d53e5b
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/modal/upload-customization.hbs
@@ -0,0 +1,8 @@
+<form {{action "dummy" on="submit"}}>
+  <div class='modal-body'>
+    {{json-file-uploader value=customizationFile extension=".dcstyle.json"}}
+  </div>
+  <div class="modal-footer">
+    {{d-button class='btn-primary' action='createCustomization' type='submit' disabled=notReady icon="plus" label='admin.customize.import'}}
+  </div>
+</form>
diff --git a/app/assets/javascripts/discourse/views/upload-customization.js.es6 b/app/assets/javascripts/discourse/views/upload-customization.js.es6
new file mode 100644
index 000000000..c6e336157
--- /dev/null
+++ b/app/assets/javascripts/discourse/views/upload-customization.js.es6
@@ -0,0 +1,6 @@
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
+  templateName: 'modal/upload-customization',
+  title: I18n.t('admin.customize.import_title')
+});
diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss
index ff1067874..61980809a 100644
--- a/app/assets/stylesheets/common/admin/admin_base.scss
+++ b/app/assets/stylesheets/common/admin/admin_base.scss
@@ -545,6 +545,9 @@ section.details {
     .preview-link {
       margin-left: 15px;
     }
+    .export {
+      float: right;
+    }
     padding-left: 10px;
     width: 70%;
     .style-name {
diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss
index 7f31ee5d9..16fc2cde9 100644
--- a/app/assets/stylesheets/common/base/modal.scss
+++ b/app/assets/stylesheets/common/base/modal.scss
@@ -138,6 +138,55 @@
     .raw-email-textarea {
       height: 300px;
     }
+    .json-uploader {
+      .jsfu-shade-container {
+        display: table-row;
+        width: 100%;
+        height: 100%;
+        position: relative;
+      }
+      .jsfu-shade {
+        z-index: 1;
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        background-color: rgba(0, 0, 0, 0.5);
+        .text {
+          color: rgb(255,255,255);
+          position: absolute;
+          top: 40%;
+          font-size: 36px;
+          text-align: center;
+          line-height: 38px;
+          margin-left: auto;
+          margin-right: auto;
+          left: 0;
+          right: 0;
+        }
+      }
+      .jsfu-file {
+        display: table-cell;
+        vertical-align: middle;
+        min-width: 120px;
+      }
+      .jsfu-separator {
+        vertical-align: middle;
+        display: table-cell;
+        font-size: 36px;
+        padding-left: 10px;
+        padding-right: 10px;
+      }
+      .jsfu-paste {
+        display: table-cell;
+        width: 100%;
+        textarea {
+          margin-bottom: 0;
+          margin-top: 4px;
+        }
+      }
+    }
   }
   .password-confirmation {
     display: none;
diff --git a/app/controllers/admin/site_customizations_controller.rb b/app/controllers/admin/site_customizations_controller.rb
index dc104fd74..30929fe4e 100644
--- a/app/controllers/admin/site_customizations_controller.rb
+++ b/app/controllers/admin/site_customizations_controller.rb
@@ -2,6 +2,8 @@ class Admin::SiteCustomizationsController < Admin::AdminController
 
   before_filter :enable_customization
 
+  skip_before_filter :check_xhr, only: [:show]
+
   def index
     @site_customizations = SiteCustomization.order(:name)
 
@@ -48,6 +50,26 @@ class Admin::SiteCustomizationsController < Admin::AdminController
     end
   end
 
+  def show
+    @site_customization = SiteCustomization.find(params[:id])
+
+    respond_to do |format|
+      format.json do
+        check_xhr
+        render json: SiteCustomizationSerializer.new(@site_customization)
+      end
+
+      format.any(:html, :text) do
+        raise RenderEmpty.new if request.xhr?
+
+        response.headers['Content-Disposition'] = "attachment; filename=#{@site_customization.name.parameterize}.dcstyle.json"
+        response.sending_file = true
+        render json: SiteCustomizationSerializer.new(@site_customization)
+      end
+    end
+
+  end
+
   private
 
     def site_customization_params
diff --git a/app/serializers/site_customization_serializer.rb b/app/serializers/site_customization_serializer.rb
new file mode 100644
index 000000000..1c8ff8f9d
--- /dev/null
+++ b/app/serializers/site_customization_serializer.rb
@@ -0,0 +1,7 @@
+class SiteCustomizationSerializer < ApplicationSerializer
+
+  attributes :id, :name, :key, :enabled, :created_at, :updated_at,
+             :stylesheet, :header, :footer, :top,
+             :mobile_stylesheet, :mobile_header, :mobile_footer, :mobile_top,
+             :head_tag, :body_tag
+end
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 3ee218f28..6d49c48c0 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -153,6 +153,7 @@ en:
     every_two_weeks: "every two weeks"
     every_three_days: "every three days"
     max_of_count: "max of {{count}}"
+    alternation: "or"
     character_count:
       one: "{{count}} character"
       other: "{{count}} characters"
@@ -863,6 +864,7 @@ en:
       hint: "(you can also drag & drop into the editor to upload them)"
       hint_for_supported_browsers: "(you can also drag and drop or paste images into the editor to upload them)"
       uploading: "Uploading"
+      select_file: "Select File"
       image_link: "link your image will point to"
 
     search:
@@ -1880,6 +1882,8 @@ en:
           screened_email: "Export full screened email list in CSV format."
           screened_ip: "Export full screened IP list in CSV format."
           screened_url: "Export full screened URL list in CSV format."
+      export_json:
+        button_text: "Export"
 
       invite:
         button_text: "Send Invites"
@@ -1909,6 +1913,8 @@ en:
         save: "Save"
         new: "New"
         new_style: "New Style"
+        import: "Import"
+        import_title: "Select a file or paste text"
         delete: "Delete"
         delete_confirm: "Delete this customization?"
         about: "Modify CSS stylesheets and HTML headers on the site. Add a customization to start."