diff --git a/core/tests/test_import_views.py b/core/tests/test_import_views.py
index dd5c99341256c032cb6edf8d965c829a1b352bd7..e5d4888194aa405f315fdc98946dc7fda7be262a 100644
--- a/core/tests/test_import_views.py
+++ b/core/tests/test_import_views.py
@@ -126,7 +126,10 @@ class ImportStudentsApiViewSetTest(APITestCase):
         self.url = '/api/import-students/'
         self.client = APIClient()
         self.client.force_login(user=self.factory.make_reviewer())
-        ExamType.objects.get_or_create(module_reference='john and jane does exam', total_score=10, pass_score=5)
+        ExamType.objects.get_or_create(
+            module_reference='john and jane does exam',
+            total_score=10, pass_score=5
+        )
 
     def test_can_not_submit_nothing(self):
         res = self.client.post(self.url)
diff --git a/frontend/src/components/import/HektorImportDialog.vue b/frontend/src/components/import/HektorImportDialog.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e81ac8ad3c61aa5a423e079331230cb3fe6555d8
--- /dev/null
+++ b/frontend/src/components/import/HektorImportDialog.vue
@@ -0,0 +1,71 @@
+<template>
+  <v-dialog
+    v-model="show"
+    width="30%"
+  >
+    <v-card>
+      <v-card-title class="title">
+        Import data
+      </v-card-title>
+      <v-card-text>
+        <p>
+          You can use this component to import data into Grady.
+          You can use
+          <a
+            href="https://gitlab.gwdg.de/grady-corp/rusty-hektor"
+            target="_blank"
+          >rusty-hektor</a> to convert
+          and pseudonomize ILIAS output.
+        </p>
+        <file-select
+          v-model="importFile"
+          display-text="Select json file"
+        />
+      </v-card-text>
+      <v-card-actions>
+        <v-btn
+          id="submit-import"
+          :loading="loading"
+          @click="submitData"
+        >
+          Import
+        </v-btn>
+        <v-btn
+          color="red"
+          @click="$emit('hide')"
+        >
+          Cancel
+        </v-btn>
+      </v-card-actions>
+    </v-card>
+  </v-dialog>
+</template>
+
+<script>
+  import FileSelect from '@/components/util/FileSelect.vue'
+  import { importHektorData } from '@/api'
+  import importJSONMixin from '@/components/mixins/importJSONMixin'
+
+  export default {
+    name: 'HektorImportDialog',
+    components: {
+      FileSelect
+    },
+    mixins: [importJSONMixin],
+    data: () => {
+      return {
+        show: true,
+        loading: false,
+        importFile: null,
+        importAction: importHektorData
+      }
+    },
+    watch: {
+      show(val) {
+        if (!val) {
+          this.$emit('hide')
+        }
+      }
+    },
+  }
+</script>
diff --git a/frontend/src/components/mixins/importJSONMixin.ts b/frontend/src/components/mixins/importJSONMixin.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4d6c4b0ee8e89e465b2809025e6e2ef0572782b5
--- /dev/null
+++ b/frontend/src/components/mixins/importJSONMixin.ts
@@ -0,0 +1,54 @@
+import Vue from 'vue'
+
+export default Vue.extend({
+  data: () => {
+    return {
+      show: true,
+      loading: false,
+      importFile: new Blob(),
+      importAction: async (data: string) => {}
+    }
+  },
+  methods: {
+    async submitData() {
+      this.loading = true
+      let data
+      try {
+        data = await this.readFile()
+        data = JSON.parse(data)
+      } catch (error) {
+        this.$notify({
+          type: 'error',
+          title: 'Error reading import file',
+          text: error.message
+        })
+        this.loading = false
+      }
+
+      try {
+        await this.importAction(data)
+        this.$emit('imported')
+        this.$notify({
+            title: 'Successfully imported data. Please log out and in again.',
+            type: 'success'
+        })
+      } finally {
+        this.loading = false
+      }
+    },
+    readFile(): Promise<string> {
+      const fileReader = new FileReader()
+      return new Promise((resolve, reject) => {
+        fileReader.onload = event => {
+          const target = event.target as FileReader
+          resolve(target.result as string)
+        }
+        fileReader.onerror = () => {
+          fileReader.abort()
+          reject(new Error('Problem parsing input file.'))
+        }
+        fileReader.readAsText(this.importFile)
+      })
+    }
+  }
+})
\ No newline at end of file