diff --git a/frontend/src/editor/DefinitionUploader/index.tsx b/frontend/src/editor/DefinitionUploader/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..cfa98556cd0b17016bc789f1c1de791a414c7315
--- /dev/null
+++ b/frontend/src/editor/DefinitionUploader/index.tsx
@@ -0,0 +1,99 @@
+import { useNavigate } from '@/router'
+import {
+  Button,
+  ButtonGroup,
+  Classes,
+  Dialog,
+  DialogBody,
+  DialogFooter,
+  FileInput,
+} from '@blueprintjs/core'
+import type { ChangeEvent } from 'react'
+import { memo, useCallback, useState } from 'react'
+import TooltipLabel from '../Tooltips/TooltipLabel'
+import {
+  DEFINITION_UPLOAD_FORM,
+  LANDING_PAGE_ACTIONS,
+} from '../assets/pageContent/landingPage'
+import { clearDb } from '../indexeddb/operations'
+import useEditorStorage from '../useEditorStorage'
+import { loadDbData, loadEditorConfig } from '../zip/loadDefinitionZip'
+
+const DefinitionUploader = () => {
+  const [isOpen, setIsOpen] = useState(false)
+  const [file, setFile] = useState<File | undefined>()
+  const [, setConfig] = useEditorStorage()
+  const nav = useNavigate()
+
+  const handleAddButton = useCallback(async (zip: File) => {
+    clearDb()
+
+    const config = await loadEditorConfig(zip)
+    const finalMilestoneId = await loadDbData(zip)
+
+    setConfig({ ...config, finalMilestone: finalMilestoneId })
+    nav('/editor/create/introduction')
+  }, [])
+
+  const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
+    if (e.target.files) {
+      setFile(e.target.files[0])
+    }
+  }
+
+  return (
+    <>
+      <Button
+        text={LANDING_PAGE_ACTIONS.upload}
+        icon='import'
+        onClick={() => setIsOpen(true)}
+      />
+      <Dialog
+        isOpen={isOpen}
+        onClose={() => setIsOpen(false)}
+        icon='import'
+        title='Upload definition'
+      >
+        <DialogBody>
+          <p>
+            This will permanently delete all data in the editor. Are you sure
+            you want to upload new definition?
+          </p>
+          <TooltipLabel label={DEFINITION_UPLOAD_FORM.file}>
+            <div
+              style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-end' }}
+            >
+              <FileInput
+                className={Classes.INPUT}
+                fill
+                hasSelection={file !== undefined}
+                text={file ? file.name : 'Choose file...'}
+                onInputChange={handleFileChange}
+              />
+            </div>
+          </TooltipLabel>
+        </DialogBody>
+        <DialogFooter
+          actions={
+            <ButtonGroup>
+              <Button
+                onClick={() => setIsOpen(false)}
+                icon='cross'
+                text='Cancel'
+              />
+              <Button
+                disabled={!file}
+                onClick={() => handleAddButton(file!)}
+                intent='primary'
+                icon='plus'
+                text='Add'
+              />
+            </ButtonGroup>
+          }
+        />
+      </Dialog>
+    </>
+  )
+}
+
+export default memo(DefinitionUploader)
diff --git a/frontend/src/editor/DownloadDefinition/index.tsx b/frontend/src/editor/DownloadDefinition/index.tsx
index da0bb69a92658fcc7be56422a2d808286d1b51e0..97bcf506af33e1bd7b0ed5204ef230734cfa7861 100644
--- a/frontend/src/editor/DownloadDefinition/index.tsx
+++ b/frontend/src/editor/DownloadDefinition/index.tsx
@@ -1,5 +1,5 @@
-import { downloadDefinitionZip } from '@/editor/downloadDefinitionZip'
 import useEditorStorage from '@/editor/useEditorStorage'
+import { createDefinitionZip } from '@/editor/zip/createDefinitionZip'
 import { Button, ButtonGroup } from '@blueprintjs/core'
 import { memo } from 'react'
 import DataRemovalDialog from '../DataRemovalDialog'
@@ -8,7 +8,7 @@ const DownloadDefinition = () => {
   const [config] = useEditorStorage()
 
   const handleDownloadYaml = async () => {
-    downloadDefinitionZip(config || {})
+    createDefinitionZip(config || {})
   }
 
   return (
diff --git a/frontend/src/editor/LandingPage/index.tsx b/frontend/src/editor/LandingPage/index.tsx
index 00ddcc1106bcc57cec929afa68dea2f4dc6a97c1..6d8c7fa2f0dcbcc27733f931bcdf64e6417adedd 100644
--- a/frontend/src/editor/LandingPage/index.tsx
+++ b/frontend/src/editor/LandingPage/index.tsx
@@ -6,6 +6,7 @@ import Container from '@inject/shared/components/Container'
 import { isEmpty } from 'lodash'
 import { useEffect, useState } from 'react'
 import DataRemovalDialog from '../DataRemovalDialog'
+import DefinitionUploader from '../DefinitionUploader'
 import { LANDING_PAGE_ACTIONS } from '../assets/pageContent/landingPage'
 import { PAGE_INFORMATION } from '../assets/pageInformation'
 import { isDbEmpty } from '../indexeddb/operations'
@@ -76,6 +77,7 @@ const LandingPage = () => {
             redirectTo='/editor/create/introduction'
           />
         )}
+        <DefinitionUploader />
       </div>
     </Container>
   )
diff --git a/frontend/src/editor/assets/pageContent/landingPage.tsx b/frontend/src/editor/assets/pageContent/landingPage.tsx
index a04192ff0bde41a8b1dca1d2b662a6c768a13423..b03e31db37f58e107131fafbd67bd731e08965aa 100644
--- a/frontend/src/editor/assets/pageContent/landingPage.tsx
+++ b/frontend/src/editor/assets/pageContent/landingPage.tsx
@@ -1,4 +1,15 @@
+import type { Form } from '@/editor/types'
+
 export const LANDING_PAGE_ACTIONS = {
   create: 'Create',
   edit: 'Continue editing',
+  upload: 'Upload',
+}
+
+export const DEFINITION_UPLOAD_FORM: Form = {
+  file: {
+    label: 'Definition',
+    tooltip: '',
+    optional: true,
+  },
 }
diff --git a/frontend/src/editor/indexeddb/db.tsx b/frontend/src/editor/indexeddb/db.tsx
index cd4c017a3684d1db290a24222f4897d80d31cc6f..745f012bf0704963ebe1efbbf008e90655360cc5 100644
--- a/frontend/src/editor/indexeddb/db.tsx
+++ b/frontend/src/editor/indexeddb/db.tsx
@@ -9,6 +9,7 @@ import type {
   InjectInfo,
   LearningActivityInfo,
   LearningObjectiveInfo,
+  MarkdownContent,
   Milestone,
   Overlay,
   Questionnaire,
@@ -36,6 +37,7 @@ const db = new Dexie(dbName) as Dexie & {
   injectControls: EntityTable<InjectControl, 'id'>
   files: EntityTable<ContentFile, 'id'>
   milestones: EntityTable<Milestone, 'id'>
+  markdownContents: EntityTable<MarkdownContent, 'id'>
 }
 
 db.version(dbVersion).stores({
@@ -45,18 +47,19 @@ db.version(dbVersion).stores({
   tools: '++id, &name, tooltipDescription, hint, defaultResponse, category',
   toolResponses:
     '++id, &learningActivityId, toolId, parameter, isRegex, content, fileId',
-  emailAddresses: '++id, address, organization, description, teamVisible',
+  emailAddresses: '++id, &address, organization, description, teamVisible',
   emailTemplates:
     '++id, &learningActivityId, emailAddressId, context, content, fileId',
   emailInjects:
     '++id, &injectInfoId, emailAddressId, subject, content, extraCopies, fileId',
   informationInjects: '++id, &injectInfoId, content, fileId',
-  questionnaires: '++id, &injectInfoId, title',
+  questionnaires: '++id, &injectInfoId, &title',
   questionnaireQuestions: '++id, questionnaireId, text, max, correct, labels',
   overlays: '++id, &injectInfoId, duration',
   injectControls: '++id, &injectInfoId, start, delay, milestoneCondition',
   files: '++id, &name, blob',
   milestones: '++id, [type+referenceId]',
+  markdownContents: '++id, &fileName, content',
 })
 
 export { db }
diff --git a/frontend/src/editor/indexeddb/joins.tsx b/frontend/src/editor/indexeddb/joins.tsx
index d96e7c0bcc8094043a973398b3edeed8bde992a9..071826da3ee612d9a5ab2ddea185cdea0921027a 100644
--- a/frontend/src/editor/indexeddb/joins.tsx
+++ b/frontend/src/editor/indexeddb/joins.tsx
@@ -13,6 +13,7 @@ import {
   getLearningActivityById,
   getOverlayByInjectInfoId,
   getQuestionnaireByInjectInfoId,
+  getToolById,
   getToolResponseById,
 } from './operations'
 import type {
@@ -44,16 +45,23 @@ export const getMilestonesWithNames = async () => {
         }
         case MilestoneEventType.TOOL: {
           const response = await getToolResponseById(milestone.referenceId)
-          return response
-            ? {
-                ...milestone,
-                name: response.parameter,
-              }
-            : null
+          if (!response) return
+
+          const tool = await getToolById(response?.id)
+          return {
+            ...milestone,
+            name: `${tool ? `${tool?.name} ` : ''}${response.parameter}`,
+          }
         }
         case MilestoneEventType.EMAIL: {
           const template = await getEmailTemplateById(milestone.referenceId)
-          return template ? { ...milestone, name: template.context } : null
+          if (!template) return
+
+          const address = await getEmailAddressById(template.emailAddressId)
+          return {
+            ...milestone,
+            name: `${address ? `${address?.address} ` : ''}${template.context}`,
+          }
         }
         default:
           return
diff --git a/frontend/src/editor/indexeddb/operations.tsx b/frontend/src/editor/indexeddb/operations.tsx
index 838b9564c19bb98ac402a668649383e3631bf25c..e893cf1e6aa34fa93e2ca2d9a9a003b5c9e7c1ed 100644
--- a/frontend/src/editor/indexeddb/operations.tsx
+++ b/frontend/src/editor/indexeddb/operations.tsx
@@ -1,5 +1,6 @@
 import { isEmpty } from 'lodash'
 import { db } from './db'
+import type { MarkdownContent } from './types'
 import {
   InjectType,
   LearningActivityType,
@@ -38,7 +39,8 @@ export const addLearningObjective = async (
   objective: Omit<LearningObjectiveInfo, 'id'>
 ) =>
   await db.transaction('rw', db.learningObjectives, async () => {
-    await db.learningObjectives.add(objective)
+    const id = await db.learningObjectives.add(objective)
+    return id
   })
 
 export const updateLearningObjective = async (
@@ -69,6 +71,9 @@ export const deleteLearningObjective = async (id: number) =>
 export const getLearningActivityById = async (id: number) =>
   await db.learningActivities.get(id)
 
+export const getLearningActivityByName = async (name: string) =>
+  await db.learningActivities.get({ name })
+
 export const addLearningActivity = async (
   activity: Omit<LearningActivityInfo, 'id'>
 ) =>
@@ -78,6 +83,7 @@ export const addLearningActivity = async (
       type: MilestoneEventType.LEARNING_ACTIVITY,
       referenceId: id,
     })
+    return id
   })
 
 export const updateLearningActivity = async (activity: LearningActivityInfo) =>
@@ -149,6 +155,7 @@ export const addInjectInfo = async (injectInfo: Omit<InjectInfo, 'id'>) =>
   await db.transaction('rw', db.injectInfos, db.milestones, async () => {
     const id = await db.injectInfos.add(injectInfo)
     await addMilestone({ type: MilestoneEventType.INJECT, referenceId: id })
+    return id
   })
 
 export const updateInjectInfo = async (injectInfo: InjectInfo) =>
@@ -251,11 +258,17 @@ export const addToolResponse = async (response: Omit<ToolResponse, 'id'>) =>
     if (!response.learningActivityId) {
       await addMilestone({ type: MilestoneEventType.TOOL, referenceId: id })
     }
+    return id
   })
 
 export const updateToolResponse = async (response: ToolResponse) =>
   await db.toolResponses.put(response)
 
+export const updateToolResponseMilestoneCondition = async (
+  id: number,
+  milestoneCondition: number[]
+) => await db.toolResponses.update(id, { milestoneCondition })
+
 export const deleteToolResponse = async (id: number) =>
   await db.transaction('rw', db.toolResponses, db.milestones, async () => {
     await db.toolResponses.delete(id)
@@ -280,6 +293,9 @@ export const doToolsHaveResponses = async () => {
 export const getEmailAddressById = async (id: number) =>
   await db.emailAddresses.get(id)
 
+export const getEmailAddressByName = async (address: string) =>
+  await db.emailAddresses.get({ address })
+
 export const addEmailAddress = async (address: Omit<EmailAddressInfo, 'id'>) =>
   await db.transaction('rw', db.emailAddresses, async () => {
     const id = await db.emailAddresses.add(address)
@@ -319,6 +335,7 @@ export const addEmailTemplate = async (template: Omit<EmailTemplate, 'id'>) =>
     if (!template.learningActivityId) {
       await addMilestone({ type: MilestoneEventType.EMAIL, referenceId: id })
     }
+    return id
   })
 
 export const updateEmailTemplate = async (template: EmailTemplate) =>
@@ -374,7 +391,8 @@ export const addQuestionnaire = async (
   questionnaire: Omit<Questionnaire, 'id'>
 ) =>
   await db.transaction('rw', db.questionnaires, async () => {
-    await db.questionnaires.add(questionnaire)
+    const id = await db.questionnaires.add(questionnaire)
+    return id
   })
 
 export const updateQuestionnaire = async (questionnaire: Questionnaire) =>
@@ -440,6 +458,9 @@ export const deleteInjectControl = async (id: number) =>
 // file operations
 export const getFileById = async (id: number) => await db.files.get(id)
 
+export const getFileByName = async (name: string) =>
+  await db.files.get({ name })
+
 export const addFile = async (file: Omit<ContentFile, 'id'>) =>
   await db.transaction('rw', db.files, async () => {
     const id = await db.files.add(file)
@@ -454,6 +475,11 @@ export const deleteFile = async (id: number) => await db.files.delete(id)
 export const getMilestoneById = async (id: number) =>
   await db.milestones.get(id)
 
+export const getMilestoneByTypeAndReferenceId = async (
+  type: MilestoneEventType,
+  referenceId: number
+) => await db.milestones.get({ type, referenceId })
+
 export const addMilestone = async (milestone: Omit<Milestone, 'id'>) =>
   await db.transaction('rw', db.milestones, async () => {
     const id = await db.milestones.add(milestone)
@@ -465,3 +491,20 @@ export const updateMilestone = async (milestone: Milestone) =>
 
 export const deleteMilestone = async (id: number) =>
   await db.milestones.delete(id)
+
+// markdown content operations
+export const getMarkdownContentByName = async (fileName: string) =>
+  await db.markdownContents.get({ fileName })
+
+export const addMarkdownContent = async (
+  content: Omit<MarkdownContent, 'id'>
+) =>
+  await db.transaction('rw', db.markdownContents, async () => {
+    await db.markdownContents.add(content)
+  })
+
+export const updateMarkdownContent = async (content: MarkdownContent) =>
+  await db.markdownContents.put(content)
+
+export const deleteMarkdownContent = async (id: number) =>
+  await db.markdownContents.delete(id)
diff --git a/frontend/src/editor/indexeddb/types.tsx b/frontend/src/editor/indexeddb/types.tsx
index 83629f1ac0119aa6d7925500ad7dbc9b0a508875..d14f9bcfea8d3012565f8c23dfe56219803c6676 100644
--- a/frontend/src/editor/indexeddb/types.tsx
+++ b/frontend/src/editor/indexeddb/types.tsx
@@ -26,7 +26,7 @@ export type LearningObjectiveInfo = {
 export type LearningActivityInfo = {
   id: number
   name: string
-  description: string
+  description?: string
   type: LearningActivityType
   learningObjectiveId: number
 }
@@ -34,17 +34,17 @@ export type LearningActivityInfo = {
 export type InjectInfo = {
   id: number
   name: string
-  description: string
+  description?: string
   type: InjectType
 }
 
 export type ToolInfo = {
   id: number
   name: string
-  category: string
-  tooltipDescription: string
+  category?: string
+  tooltipDescription?: string
   defaultResponse: string
-  hint: string
+  hint?: string
 }
 
 export type ToolResponse = {
@@ -52,10 +52,10 @@ export type ToolResponse = {
   learningActivityId?: number
   toolId: number
   parameter: string
-  isRegex: boolean
-  content: string
+  isRegex?: boolean
+  content?: string
   fileId?: number
-  time: number
+  time?: number
   milestoneCondition?: number[]
 }
 
@@ -63,8 +63,8 @@ export type EmailAddressInfo = {
   id: number
   address: string
   description: string
-  teamVisible: boolean
-  organization: string
+  teamVisible?: boolean
+  organization?: string
 }
 
 export type EmailTemplate = {
@@ -72,7 +72,7 @@ export type EmailTemplate = {
   learningActivityId?: number
   emailAddressId: number
   context: string
-  content: string
+  content?: string
   fileId?: number
 }
 
@@ -81,15 +81,15 @@ export type EmailInject = {
   injectInfoId: number
   emailAddressId: number
   subject: string
-  content: string
-  extraCopies: number
+  content?: string
+  extraCopies?: number
   fileId?: number
 }
 
 export type InformationInject = {
   id: number
   injectInfoId: number
-  content: string
+  content?: string
   fileId?: number
 }
 
@@ -104,15 +104,15 @@ export type QuestionnaireQuestion = {
   questionnaireId: number
   text: string
   max: number
-  correct: number
-  labels: string
+  correct?: number
+  labels?: string
 }
 
 export type InjectControl = {
   id: number
   injectInfoId: number
-  start: number
-  delay: number
+  start?: number
+  delay?: number
   milestoneCondition?: number[]
 }
 
@@ -134,6 +134,12 @@ export type Milestone = {
   referenceId: number
 }
 
+export type MarkdownContent = {
+  id: number
+  fileName: string
+  content: string
+}
+
 // joined types
 export type MilestoneName = Milestone & {
   name: string
diff --git a/frontend/src/editor/utils.tsx b/frontend/src/editor/utils.tsx
index 95e18f77a3dcfe51f045ff5679aca386b4eda64c..40d47494d036987952ed37f3d08d38732a3d8535 100644
--- a/frontend/src/editor/utils.tsx
+++ b/frontend/src/editor/utils.tsx
@@ -45,11 +45,10 @@ export const DEFAULT_OPTION: OptionProps = { value: '0', label: '+' }
 export const OPENING_BRACKET: OptionProps = { value: '-1', label: '(' }
 export const CLOSING_BRACKET: OptionProps = { value: '-2', label: ')' }
 export const NOT: OptionProps = { value: '-3', label: 'not' }
-export const OPERATORS: OptionProps[] = [
-  { value: '-4', label: 'and' },
-  { value: '-5', label: 'or' },
-]
-const ALL_OPERATORS: OptionProps[] = [
+export const AND: OptionProps = { value: '-4', label: 'and' }
+export const OR: OptionProps = { value: '-5', label: 'or' }
+export const OPERATORS: OptionProps[] = [AND, OR]
+export const ALL_OPERATORS: OptionProps[] = [
   CLOSING_BRACKET,
   OPENING_BRACKET,
   NOT,
@@ -69,7 +68,7 @@ export const getVariables = (milestones: MilestoneName[]) =>
     label: getMilestoneName(milestone),
   }))
 
-const getPrefixByMilestoneType = (type: MilestoneEventType) => {
+export const getPrefixByMilestoneType = (type: MilestoneEventType) => {
   switch (type) {
     case MilestoneEventType.LEARNING_ACTIVITY:
       return 'la'
@@ -85,7 +84,7 @@ const getPrefixByMilestoneType = (type: MilestoneEventType) => {
 }
 
 export const getMilestoneName = (milestone: MilestoneName) =>
-  `${getPrefixByMilestoneType(milestone.type)}_${milestone.name.replace(' ', '_')}${milestone.type === MilestoneEventType.TOOL || milestone.type === MilestoneEventType.EMAIL ? milestone.id : ''}`
+  `${getPrefixByMilestoneType(milestone.type)}_${milestone.name.replace(/\s+/g, '_')}${milestone.type === MilestoneEventType.TOOL || milestone.type === MilestoneEventType.EMAIL ? `_${milestone.id}` : ''}`
 
 type ValidationResult = {
   isValid: boolean
diff --git a/frontend/src/editor/yaml/generate/email.tsx b/frontend/src/editor/yaml/generate/email.tsx
index ba8eaf9213c1a5034c09ecd199545ce2477e7bca..ae99971d87c5094b3842caa23238a544eedd3b42 100644
--- a/frontend/src/editor/yaml/generate/email.tsx
+++ b/frontend/src/editor/yaml/generate/email.tsx
@@ -1,18 +1,24 @@
 import { getEmailAddresses } from '@/editor/indexeddb/joins'
-import type {
-  JoinedEmailAddress,
-  JoinedEmailTemplate,
+import {
+  type JoinedEmailAddress,
+  type JoinedEmailTemplate,
 } from '@/editor/indexeddb/types'
 import { isEmpty, pickBy } from 'lodash'
 import { generateContent, generateControl } from './shared'
 
-const generateTemplates = (emailTemplates: JoinedEmailTemplate[]) => {
-  const generatedTemplates = emailTemplates.map(emailTemplate =>
-    pickBy({
-      context: emailTemplate.context,
-      content: generateContent(emailTemplate.content, emailTemplate.file),
-      control: generateControl(emailTemplate.activateMilestone),
-    })
+const generateTemplates = async (emailTemplates: JoinedEmailTemplate[]) => {
+  const generatedTemplates = await Promise.all(
+    emailTemplates.map(async emailTemplate =>
+      pickBy({
+        context: emailTemplate.context,
+        content: await generateContent(
+          `et_${emailTemplate.id}`,
+          emailTemplate.content,
+          emailTemplate.file
+        ),
+        control: generateControl(emailTemplate.activateMilestone),
+      })
+    )
   )
 
   return isEmpty(generatedTemplates) ? undefined : generatedTemplates
@@ -21,15 +27,16 @@ const generateTemplates = (emailTemplates: JoinedEmailTemplate[]) => {
 export const generateEmailAddresses = async () => {
   const emailAddresses = await getEmailAddresses()
 
-  const generatedAddresses = emailAddresses.map(
-    (emailAddress: JoinedEmailAddress) =>
+  const generatedAddresses = await Promise.all(
+    emailAddresses.map(async (emailAddress: JoinedEmailAddress) =>
       pickBy({
         address: emailAddress.address,
         team_visible: emailAddress.teamVisible,
         description: emailAddress.description,
         organization: emailAddress.organization,
-        templates: generateTemplates(emailAddress.emailTemplates),
+        templates: await generateTemplates(emailAddress.emailTemplates),
       })
+    )
   )
 
   return isEmpty(generatedAddresses) ? undefined : generatedAddresses
diff --git a/frontend/src/editor/yaml/generate/injects.tsx b/frontend/src/editor/yaml/generate/injects.tsx
index f6d38131b467cc1e5814c4421bb3e9e95130b773..66ebd902dcb6fc33d6e58a57a92e8d9fe6f21d8d 100644
--- a/frontend/src/editor/yaml/generate/injects.tsx
+++ b/frontend/src/editor/yaml/generate/injects.tsx
@@ -2,34 +2,34 @@ import {
   getEmailInjects,
   getInformationInjects,
 } from '@/editor/indexeddb/joins'
-import type {
-  JoinedEmailInject,
-  JoinedEmailInjectCategory,
-  JoinedInformationInject,
-  JoinedInformationInjectCategory,
+import {
+  type JoinedEmailInject,
+  type JoinedEmailInjectCategory,
+  type JoinedInformationInject,
+  type JoinedInformationInjectCategory,
 } from '@/editor/indexeddb/types'
 import notEmpty from '@inject/shared/utils/notEmpty'
 import { isEmpty, pickBy } from 'lodash'
 import { generateContent, generateControl, generateOverlay } from './shared'
 
-const generateInformationInject = (
+const generateInformationInject = async (
   name: string,
   info: JoinedInformationInject
 ) =>
   pickBy({
     name: name,
-    content: generateContent(info.content, info.file),
+    content: await generateContent(`ii_${info.id}`, info.content, info.file),
     control: generateControl(info.activateMilestone, info.milestoneCondition),
     overlay: generateOverlay(info.overlay),
   })
 
-const generateEmailInject = (name: string, email: JoinedEmailInject) =>
+const generateEmailInject = async (name: string, email: JoinedEmailInject) =>
   pickBy({
     name: name,
     sender: email.sender?.address,
     subject: email.subject,
     extra_copies: email.extraCopies,
-    content: generateContent(email.content, email.file),
+    content: await generateContent(`ei_${email.id}`, email.content, email.file),
     control: generateControl(email.activateMilestone, email.milestoneCondition),
     overlay: generateOverlay(email.overlay),
   })
@@ -38,29 +38,35 @@ export const generateInjects = async () => {
   const informationInjects = await getInformationInjects()
   const emailInjects = await getEmailInjects()
 
-  const generatedInformationInjects = informationInjects
-    .filter(notEmpty)
-    .map((inject: JoinedInformationInjectCategory) =>
-      pickBy({
-        name: inject.name,
-        time: inject.injectControl?.start,
-        delay: inject.injectControl?.delay,
-        type: 'info',
-        alternatives: [generateInformationInject(inject.name, inject.info)],
-      })
-    )
+  const generatedInformationInjects = await Promise.all(
+    informationInjects
+      .filter(notEmpty)
+      .map(async (inject: JoinedInformationInjectCategory) =>
+        pickBy({
+          name: inject.name,
+          time: inject.injectControl?.start,
+          delay: inject.injectControl?.delay,
+          type: 'info',
+          alternatives: [
+            await generateInformationInject(inject.name, inject.info),
+          ],
+        })
+      )
+  )
 
-  const generatedEmailInjects = emailInjects
-    .filter(notEmpty)
-    .map((inject: JoinedEmailInjectCategory) =>
-      pickBy({
-        name: inject.name,
-        time: inject.injectControl?.start,
-        delay: inject.injectControl?.delay,
-        type: 'email',
-        alternatives: [generateEmailInject(inject.name, inject.email)],
-      })
-    )
+  const generatedEmailInjects = await Promise.all(
+    emailInjects
+      .filter(notEmpty)
+      .map(async (inject: JoinedEmailInjectCategory) =>
+        pickBy({
+          name: inject.name,
+          time: inject.injectControl?.start,
+          delay: inject.injectControl?.delay,
+          type: 'email',
+          alternatives: [await generateEmailInject(inject.name, inject.email)],
+        })
+      )
+  )
 
   const generatedInjects = [
     ...generatedInformationInjects,
diff --git a/frontend/src/editor/yaml/generate/questionnaires.tsx b/frontend/src/editor/yaml/generate/questionnaires.tsx
index 1f496a70e16873517cac43f2cfe9dc4cdabb4ec2..db50e2556ed670e579a4c409fc8b8ed2cfcf43a1 100644
--- a/frontend/src/editor/yaml/generate/questionnaires.tsx
+++ b/frontend/src/editor/yaml/generate/questionnaires.tsx
@@ -7,35 +7,44 @@ import notEmpty from '@inject/shared/utils/notEmpty'
 import { isEmpty, pickBy } from 'lodash'
 import { generateContent, generateControl, generateOverlay } from './shared'
 
-const generateQuestionnaireQuestions = (
+const generateQuestionnaireQuestions = async (
   questionnaireQuestions: QuestionnaireQuestion[]
 ) =>
-  questionnaireQuestions.map(questionnaireQuestion =>
-    pickBy({
-      content: generateContent(questionnaireQuestion.text),
-      max: questionnaireQuestion.max,
-      correct: questionnaireQuestion.correct,
-      labels: questionnaireQuestion.labels,
-    })
+  await Promise.all(
+    questionnaireQuestions.map(async questionnaireQuestion =>
+      pickBy({
+        content: await generateContent(
+          `qq_${questionnaireQuestion.id}`,
+          questionnaireQuestion.text
+        ),
+        max: questionnaireQuestion.max,
+        correct: questionnaireQuestion.correct,
+        labels: questionnaireQuestion.labels,
+      })
+    )
   )
 
 export const generateQuestionnaires = async () => {
   const questionnaires = await getQuestionnaires()
 
-  const generatedQuestionnaires = questionnaires
-    .filter(notEmpty)
-    .map((questionnaire: JoinedQuestionnaireInject) =>
-      pickBy({
-        title: questionnaire.title,
-        time: questionnaire.injectControl?.start,
-        control: generateControl(
-          questionnaire.activateMilestone,
-          questionnaire.milestoneCondition
-        ),
-        overlay: generateOverlay(questionnaire.overlay),
-        questions: generateQuestionnaireQuestions(questionnaire.questions),
-      })
-    )
+  const generatedQuestionnaires = await Promise.all(
+    questionnaires
+      .filter(notEmpty)
+      .map(async (questionnaire: JoinedQuestionnaireInject) =>
+        pickBy({
+          title: questionnaire.title,
+          time: questionnaire.injectControl?.start,
+          control: generateControl(
+            questionnaire.activateMilestone,
+            questionnaire.milestoneCondition
+          ),
+          overlay: generateOverlay(questionnaire.overlay),
+          questions: await generateQuestionnaireQuestions(
+            questionnaire.questions
+          ),
+        })
+      )
+  )
 
   return isEmpty(generatedQuestionnaires) ? undefined : generatedQuestionnaires
 }
diff --git a/frontend/src/editor/yaml/generate/shared.tsx b/frontend/src/editor/yaml/generate/shared.tsx
index 67de38bdcc363c49ec9e5736c4ba6e258f04daea..819eb38f39681c7c69a4b05d89601de4975fab76 100644
--- a/frontend/src/editor/yaml/generate/shared.tsx
+++ b/frontend/src/editor/yaml/generate/shared.tsx
@@ -1,3 +1,4 @@
+import { addMarkdownContent } from '@/editor/indexeddb/operations'
 import type {
   ContentFile,
   MilestoneName,
@@ -7,8 +8,17 @@ import { getMilestoneName } from '@/editor/utils'
 import type { OptionProps } from '@blueprintjs/core'
 import { isEmpty, pickBy } from 'lodash'
 
-export const generateContent = (content?: string, file?: ContentFile) => {
-  const generatedContent = pickBy({ content: content, fileName: file?.name })
+export const generateContent = async (
+  contentName: string,
+  content?: string,
+  file?: ContentFile
+) => {
+  const contentFileName = `${contentName}.md`
+  content && (await addMarkdownContent({ content, fileName: contentFileName }))
+  const generatedContent = pickBy({
+    content_path: contentFileName,
+    fileName: file?.name,
+  })
 
   return isEmpty(generatedContent) ? undefined : generatedContent
 }
diff --git a/frontend/src/editor/yaml/generate/tools.tsx b/frontend/src/editor/yaml/generate/tools.tsx
index 2930f07909022462a060c497e93633c5b1291eaa..fd434529f0b0c417e5f7f52ec8a55492420858eb 100644
--- a/frontend/src/editor/yaml/generate/tools.tsx
+++ b/frontend/src/editor/yaml/generate/tools.tsx
@@ -1,20 +1,29 @@
 import { getTools } from '@/editor/indexeddb/joins'
-import type { JoinedTool, JoinedToolResponse } from '@/editor/indexeddb/types'
+import {
+  type JoinedTool,
+  type JoinedToolResponse,
+} from '@/editor/indexeddb/types'
 import { isEmpty, pickBy } from 'lodash'
 import { generateContent, generateControl } from './shared'
 
-const generateToolResponses = (toolResponses: JoinedToolResponse[]) => {
-  const generatedResponses = toolResponses.map(toolResponse =>
-    pickBy({
-      param: toolResponse.parameter,
-      regex: toolResponse.isRegex,
-      time: toolResponse.time,
-      content: generateContent(toolResponse.content, toolResponse.file),
-      control: generateControl(
-        toolResponse.activateMilestone,
-        toolResponse.milestoneConditionWithNames
-      ),
-    })
+const generateToolResponses = async (toolResponses: JoinedToolResponse[]) => {
+  const generatedResponses = await Promise.all(
+    toolResponses.map(async toolResponse =>
+      pickBy({
+        param: toolResponse.parameter,
+        regex: toolResponse.isRegex,
+        time: toolResponse.time,
+        content: await generateContent(
+          `tr_${toolResponse.id}`,
+          toolResponse.content,
+          toolResponse.file
+        ),
+        control: generateControl(
+          toolResponse.activateMilestone,
+          toolResponse.milestoneConditionWithNames
+        ),
+      })
+    )
   )
 
   return isEmpty(generatedResponses) ? undefined : generatedResponses
@@ -22,14 +31,16 @@ const generateToolResponses = (toolResponses: JoinedToolResponse[]) => {
 
 export const generateTools = async () => {
   const tools = await getTools()
-  const generatedTools = tools.map((tool: JoinedTool) =>
-    pickBy({
-      name: tool.name,
-      tooltip_description: tool.tooltipDescription,
-      hint: tool.hint,
-      default_response: tool.defaultResponse,
-      responses: generateToolResponses(tool.toolResponses),
-    })
+  const generatedTools = await Promise.all(
+    tools.map(async (tool: JoinedTool) =>
+      pickBy({
+        name: tool.name,
+        tooltip_description: tool.tooltipDescription,
+        hint: tool.hint,
+        default_response: tool.defaultResponse,
+        responses: await generateToolResponses(tool.toolResponses),
+      })
+    )
   )
 
   return isEmpty(generatedTools) ? undefined : generatedTools
diff --git a/frontend/src/editor/yaml/parse/channels.tsx b/frontend/src/editor/yaml/parse/channels.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e7ccbeb6d210b51a1036c53e9188670871c1e68b
--- /dev/null
+++ b/frontend/src/editor/yaml/parse/channels.tsx
@@ -0,0 +1,14 @@
+import type { EditorConfig } from '@/editor/useEditorStorage'
+import type { ChannelYaml } from '../types'
+
+export const parseChannels = (
+  channels: ChannelYaml[]
+): Pick<
+  EditorConfig,
+  'infoChannelName' | 'emailChannelName' | 'toolChannelName' | 'formChannelName'
+> => ({
+  infoChannelName: channels.find(channel => channel.type === 'info')?.name,
+  emailChannelName: channels.find(channel => channel.type === 'email')?.name,
+  toolChannelName: channels.find(channel => channel.type === 'tool')?.name,
+  formChannelName: channels.find(channel => channel.type === 'form')?.name,
+})
diff --git a/frontend/src/editor/yaml/parse/config.tsx b/frontend/src/editor/yaml/parse/config.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3f5cf93674c063327f11c22b21387ac920adcdaa
--- /dev/null
+++ b/frontend/src/editor/yaml/parse/config.tsx
@@ -0,0 +1,17 @@
+import type { EditorConfig } from '@/editor/useEditorStorage'
+import type { ConfigYaml } from '../types'
+
+export const parseConfig = (
+  config: ConfigYaml
+): Pick<
+  EditorConfig,
+  | 'exerciseDuration'
+  | 'emailBetweenTeams'
+  | 'customEmailSuffix'
+  | 'showExerciseTime'
+> => ({
+  exerciseDuration: config.exercise_duration,
+  emailBetweenTeams: config.email_between_teams,
+  customEmailSuffix: config.custom_email_suffix,
+  showExerciseTime: config.show_exercise_time,
+})
diff --git a/frontend/src/editor/yaml/parse/email.tsx b/frontend/src/editor/yaml/parse/email.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6a7942a4e57d1735fcf9d1479d3b94b9c23e1ef6
--- /dev/null
+++ b/frontend/src/editor/yaml/parse/email.tsx
@@ -0,0 +1,70 @@
+import {
+  addEmailAddress,
+  addEmailTemplate,
+  getMilestoneByTypeAndReferenceId,
+} from '@/editor/indexeddb/operations'
+import { MilestoneEventType } from '@/editor/indexeddb/types'
+import type {
+  EmailAddressYaml,
+  EmailTemplateYaml,
+  MappedActivity,
+} from '../types'
+import { extractFirstMilestone } from './milestones'
+import { getContentFile, getContentText } from './shared'
+
+const loadEmailTemplates = async (
+  emailAddressId: number,
+  templates: EmailTemplateYaml[],
+  activityMilestones: MappedActivity[]
+) =>
+  await Promise.all(
+    templates.map(async template => {
+      const file = await getContentFile(template.content)
+      const content = await getContentText(template.content)
+      const learningActivityId = activityMilestones.find(
+        activity =>
+          extractFirstMilestone(template.control?.activate_milestone) ===
+          activity.milestoneName
+      )?.id
+
+      const id = await addEmailTemplate({
+        emailAddressId,
+        learningActivityId,
+        context: template.context,
+        content,
+        fileId: file?.id,
+      })
+      const milestone = learningActivityId
+        ? await getMilestoneByTypeAndReferenceId(
+            MilestoneEventType.LEARNING_ACTIVITY,
+            learningActivityId
+          )
+        : await getMilestoneByTypeAndReferenceId(MilestoneEventType.EMAIL, id)
+
+      return {
+        id,
+        milestoneId: milestone?.id,
+        milestoneName: extractFirstMilestone(
+          template.control?.activate_milestone
+        ),
+      }
+    })
+  )
+
+export const loadEmailAddresses = async (
+  activityMilestones: MappedActivity[],
+  addresses: EmailAddressYaml[]
+) =>
+  await Promise.all(
+    addresses.map(async address => {
+      const id = await addEmailAddress({
+        address: address.address,
+        teamVisible: address.team_visible,
+        description: address.description,
+        organization: address.organization,
+      })
+      return address.templates
+        ? await loadEmailTemplates(id, address.templates, activityMilestones)
+        : []
+    })
+  )
diff --git a/frontend/src/editor/yaml/parse/injects.tsx b/frontend/src/editor/yaml/parse/injects.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..9b46451ff38c1488fb8dc9d58df358d1f907a8a5
--- /dev/null
+++ b/frontend/src/editor/yaml/parse/injects.tsx
@@ -0,0 +1,179 @@
+import {
+  addEmailInject,
+  addInformationInject,
+  addInjectControl,
+  addInjectInfo,
+  getEmailAddressByName,
+  getMilestoneByTypeAndReferenceId,
+} from '@/editor/indexeddb/operations'
+import { InjectType, MilestoneEventType } from '@/editor/indexeddb/types'
+import { AND, CLOSING_BRACKET, NOT, OPENING_BRACKET } from '@/editor/utils'
+import type {
+  InjectAlternativeEmailYaml,
+  InjectAlternativeInfoYaml,
+  InjectCategoryYaml,
+  MappedInjectControl,
+  MappedMilestone,
+} from '../types'
+import { extractFirstMilestone } from './milestones'
+import {
+  getContentFile,
+  getContentText,
+  getMilestoneCondition,
+  loadOverlay,
+} from './shared'
+
+const loadInfoInject = async (
+  injectCategory: InjectCategoryYaml,
+  infoInject: InjectAlternativeInfoYaml,
+  injectInfoId: number
+) => {
+  const file = await getContentFile(infoInject.content)
+  const content = await getContentText(infoInject.content)
+
+  await addInformationInject({
+    injectInfoId,
+    content,
+    fileId: file?.id,
+  })
+  await loadOverlay(injectInfoId, infoInject.overlay)
+  const milestone = await getMilestoneByTypeAndReferenceId(
+    MilestoneEventType.INJECT,
+    injectInfoId
+  )
+
+  return {
+    id: injectInfoId,
+    milestoneId: milestone?.id,
+    milestoneName: extractFirstMilestone(
+      infoInject.control?.activate_milestone
+    ),
+    milestoneCondition: infoInject.control?.milestone_condition,
+    time: injectCategory.time,
+    delay: injectCategory.delay,
+  }
+}
+
+const loadEmailInject = async (
+  injectCategory: InjectCategoryYaml,
+  emailInject: InjectAlternativeEmailYaml,
+  injectInfoId: number
+) => {
+  const file = await getContentFile(emailInject.content)
+  const content = await getContentText(emailInject.content)
+  const sender = await getEmailAddressByName(emailInject.sender)
+
+  await addEmailInject({
+    injectInfoId,
+    emailAddressId: sender?.id || 0,
+    subject: emailInject.subject,
+    content,
+    extraCopies: emailInject.extra_copies,
+    fileId: file?.id,
+  })
+  await loadOverlay(injectInfoId, emailInject.overlay)
+  const milestone = await getMilestoneByTypeAndReferenceId(
+    MilestoneEventType.INJECT,
+    injectInfoId
+  )
+
+  return {
+    id: injectInfoId,
+    milestoneId: milestone?.id,
+    milestoneName: extractFirstMilestone(
+      emailInject.control?.activate_milestone
+    ),
+    milestoneCondition: emailInject.control?.milestone_condition,
+    time: injectCategory.time,
+    delay: injectCategory.delay,
+  }
+}
+
+export const loadInjects = async (injects: InjectCategoryYaml[]) =>
+  await Promise.all(
+    injects.map(async inject => {
+      if (!inject.type || inject.type === 'info') {
+        return await Promise.all(
+          inject.alternatives.map(async alternative => {
+            const id = await addInjectInfo({
+              name: `${inject.name}${inject.alternatives.length > 1 ? ` - ${alternative.name}` : ''}`,
+              type: InjectType.INFORMATION,
+            })
+            return await loadInfoInject(inject, alternative, id)
+          })
+        )
+      }
+      if (inject.type === 'email') {
+        return await Promise.all(
+          inject.alternatives.map(async alternative => {
+            const id = await addInjectInfo({
+              name: `${inject.name}${inject.alternatives.length > 1 ? ` - ${alternative.name}` : ''}`,
+              type: InjectType.EMAIL,
+            })
+            return await loadEmailInject(
+              inject,
+              alternative as InjectAlternativeEmailYaml,
+              id
+            )
+          })
+        )
+      }
+    })
+  )
+
+export const loadInjectControls = (
+  injectControlGroups: MappedInjectControl[][],
+  milestonesWithIds: MappedMilestone[]
+) => {
+  injectControlGroups.forEach(controlGroup => {
+    const milestoneIds = controlGroup
+      .map(control => control.milestoneId)
+      .filter(id => id !== undefined) as number[]
+    controlGroup.map(async control => {
+      await addInjectControl({
+        injectInfoId: control.id,
+        start: control.time,
+        delay: control.delay,
+        milestoneCondition: control.milestoneCondition
+          ? getInjectMilestoneCondition(
+              control.milestoneCondition,
+              milestonesWithIds,
+              milestoneIds,
+              control.milestoneId
+            )
+          : [],
+      })
+    })
+  })
+}
+
+const getInjectMilestoneCondition = (
+  condition: string,
+  milestonesWithIds: MappedMilestone[],
+  groupMilestoneIds: number[],
+  milestoneId?: number
+) => {
+  const originalCondition = getMilestoneCondition(condition, milestonesWithIds)
+  // excluding other inject alternatives
+  const exclusiveMilestoneIds = groupMilestoneIds.filter(
+    id => id !== milestoneId
+  )
+
+  if (exclusiveMilestoneIds.length) {
+    const originalConditionWithBrackets = originalCondition.length
+      ? [
+          Number(OPENING_BRACKET.value),
+          ...originalCondition,
+          Number(CLOSING_BRACKET.value),
+        ]
+      : []
+
+    return [
+      ...originalConditionWithBrackets,
+      ...exclusiveMilestoneIds
+        .map(id => [Number(AND.value), Number(NOT.value), id])
+        .flat(1),
+    ]
+  }
+  return originalCondition
+}
diff --git a/frontend/src/editor/yaml/parse/milestones.tsx b/frontend/src/editor/yaml/parse/milestones.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..9a980c58cbcb232149fa59a6db129384fffc8f3a
--- /dev/null
+++ b/frontend/src/editor/yaml/parse/milestones.tsx
@@ -0,0 +1,68 @@
+import type {
+  EmailAddressYaml,
+  MappedEmailTemplateControl,
+  MappedInjectControl,
+  MappedMilestone,
+  MappedQuestionnaireControl,
+  MappedToolResponseControl,
+  MilestoneYaml,
+  ToolYaml,
+} from '../types'
+
+export const extractFirstMilestone = (activateMilestone?: string) =>
+  activateMilestone?.split(',')[0].trim()
+
+export const filterToolMilestones = (
+  activityMilestones: MilestoneYaml[],
+  tools: ToolYaml[]
+) =>
+  activityMilestones.filter((milestone: MilestoneYaml) =>
+    tools.find((tool: ToolYaml) =>
+      tool.responses.find(
+        response =>
+          extractFirstMilestone(response.control?.activate_milestone) ===
+          milestone.name
+      )
+    )
+  )
+
+export const filterEmailMilestones = (
+  activityMilestones: MilestoneYaml[],
+  emails: EmailAddressYaml[]
+) =>
+  activityMilestones.filter((milestone: MilestoneYaml) =>
+    emails.find((email: EmailAddressYaml) =>
+      email.templates?.find(
+        template =>
+          extractFirstMilestone(template.control?.activate_milestone) ===
+          milestone.name
+      )
+    )
+  )
+
+export const extractMilestonesFromControls = (
+  toolControls: MappedToolResponseControl[],
+  emailControls: MappedEmailTemplateControl[],
+  injectControls: MappedInjectControl[],
+  questionnaireControls: MappedQuestionnaireControl[]
+) =>
+  [
+    ...toolControls.map(control => ({
+      id: control.milestoneId,
+      name: control.milestoneName,
+    })),
+    ...emailControls.map(control => ({
+      id: control.milestoneId,
+      name: control.milestoneName,
+    })),
+    ...injectControls.map(control => ({
+      id: control.milestoneId,
+      name: control.milestoneName,
+    })),
+    ...questionnaireControls.map(control => ({
+      id: control.milestoneId,
+      name: control.milestoneName,
+    })),
+  ].filter(
+    control => control.id !== undefined && control.name !== undefined
+  ) as MappedMilestone[]
diff --git a/frontend/src/editor/yaml/parse/objectives.tsx b/frontend/src/editor/yaml/parse/objectives.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..21e6e905ca72666b0e92b564f62b018ccf617bb6
--- /dev/null
+++ b/frontend/src/editor/yaml/parse/objectives.tsx
@@ -0,0 +1,92 @@
+import {
+  addLearningActivity,
+  addLearningObjective,
+} from '@/editor/indexeddb/operations'
+import type { LearningActivityInfo } from '@/editor/indexeddb/types'
+import { LearningActivityType } from '@/editor/indexeddb/types'
+import type {
+  LearningActivityYaml,
+  LearningActivityYamlWithType,
+  LearningObjectiveYaml,
+  LearningObjectiveYamlWithTypes,
+  MappedActivity,
+  MilestoneYaml,
+} from '../types'
+
+export const getActivityType = (
+  activity: LearningActivityYaml,
+  toolMilestones: MilestoneYaml[],
+  emailMilestones: MilestoneYaml[]
+) => {
+  if (
+    emailMilestones.find(
+      (milestone: MilestoneYaml) => milestone.name === activity.name
+    )
+  ) {
+    return LearningActivityType.EMAIL
+  }
+  if (
+    toolMilestones.find(
+      (milestone: MilestoneYaml) => milestone.name === activity.name
+    )
+  ) {
+    return LearningActivityType.TOOL
+  }
+  // TODO no demonstration
+  return LearningActivityType.TOOL
+}
+
+export const addTypesToActivities = (
+  objectives: LearningObjectiveYaml[],
+  toolMilestones: MilestoneYaml[],
+  emailMilestones: MilestoneYaml[]
+) =>
+  objectives.map((objective: LearningObjectiveYaml) => ({
+    ...objective,
+    activities: objective.activities.map(activity => ({
+      ...activity,
+      type: getActivityType(activity, toolMilestones, emailMilestones),
+    })),
+  }))
+
+export const addActivityIdsToActivityMilestones = (
+  activityMilestones: MilestoneYaml[],
+  activities: LearningActivityInfo[]
+): (MappedActivity | undefined)[] =>
+  activityMilestones.map((milestone: MilestoneYaml) => {
+    if (milestone.activity) {
+      const activity = activities.find(a => a.name === milestone.activity)
+      if (activity) {
+        return {
+          id: activity.id,
+          milestoneName: milestone.name,
+        }
+      }
+    }
+  })
+
+const loadActivities = async (
+  activities: LearningActivityYamlWithType[],
+  objectiveId: number
+) =>
+  Promise.all(
+    activities.map(async activity => {
+      const newActivity = {
+        name: activity.name,
+        type: activity.type,
+        learningObjectiveId: objectiveId,
+      }
+      const id = await addLearningActivity(newActivity)
+      return { id, ...newActivity }
+    })
+  )
+
+export const loadObjectives = async (
+  objectives: LearningObjectiveYamlWithTypes[]
+) =>
+  Promise.all(
+    objectives.map(async objective => {
+      const id = await addLearningObjective({ name: objective.name })
+      return await loadActivities(objective.activities, id)
+    })
+  )
diff --git a/frontend/src/editor/yaml/parse/questionnaires.tsx b/frontend/src/editor/yaml/parse/questionnaires.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..7d9f3c199b3870b1e427d18b59ae937e70c2fd78
--- /dev/null
+++ b/frontend/src/editor/yaml/parse/questionnaires.tsx
@@ -0,0 +1,78 @@
+import {
+  addInjectControl,
+  addInjectInfo,
+  addQuestionnaire,
+  addQuestionnaireQuestion,
+  getMilestoneByTypeAndReferenceId,
+} from '@/editor/indexeddb/operations'
+import { InjectType, MilestoneEventType } from '@/editor/indexeddb/types'
+import type {
+  MappedMilestone,
+  MappedQuestionnaireControl,
+  QuestionYaml,
+  QuestionnaireYaml,
+} from '../types'
+import { extractFirstMilestone } from './milestones'
+import { getContentText, getMilestoneCondition, loadOverlay } from './shared'
+
+const loadQuestions = async (
+  questions: QuestionYaml[],
+  questionnaireId: number
+) => {
+  questions.map(async question => {
+    const content = await getContentText(question.content)
+
+    await addQuestionnaireQuestion({
+      questionnaireId,
+      text: content || '',
+      max: question.max,
+      correct: question.correct,
+      labels: question.labels,
+    })
+  })
+}
+
+export const loadQuestionnaires = async (questionnaires: QuestionnaireYaml[]) =>
+  await Promise.all(
+    questionnaires.map(async questionnaire => {
+      const injectInfoId = await addInjectInfo({
+        name: questionnaire.title,
+        type: InjectType.QUESTIONNAIRE,
+      })
+      const id = await addQuestionnaire({
+        injectInfoId: injectInfoId,
+        title: questionnaire.title,
+      })
+      await loadOverlay(injectInfoId, questionnaire.overlay)
+      await loadQuestions(questionnaire.questions, id)
+      const milestone = await getMilestoneByTypeAndReferenceId(
+        MilestoneEventType.INJECT,
+        injectInfoId
+      )
+
+      return {
+        id: injectInfoId,
+        milestoneId: milestone?.id,
+        milestoneName: extractFirstMilestone(
+          questionnaire.control?.activate_milestone
+        ),
+        milestoneCondition: questionnaire.control?.milestone_condition,
+        time: questionnaire.time,
+      }
+    })
+  )
+
+export const loadQuestionnaireControls = (
+  questionnaireControls: MappedQuestionnaireControl[],
+  milestonesWithIds: MappedMilestone[]
+) => {
+  questionnaireControls.forEach(async control => {
+    await addInjectControl({
+      injectInfoId: control.id,
+      start: control.time,
+      milestoneCondition: control.milestoneCondition
+        ? getMilestoneCondition(control.milestoneCondition, milestonesWithIds)
+        : [],
+    })
+  })
+}
diff --git a/frontend/src/editor/yaml/parse/shared.tsx b/frontend/src/editor/yaml/parse/shared.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..0ea75bf953335b59aa0dab4ea244c7a9d8da2f11
--- /dev/null
+++ b/frontend/src/editor/yaml/parse/shared.tsx
@@ -0,0 +1,43 @@
+import {
+  addOverlay,
+  getFileByName,
+  getMarkdownContentByName,
+} from '@/editor/indexeddb/operations'
+import { ALL_OPERATORS } from '@/editor/utils'
+import type { ContentYaml, MappedMilestone, OverlayYaml } from '../types'
+
+export const getContentFile = async (content?: ContentYaml) => {
+  const fileName = content?.file_name
+  return fileName ? await getFileByName(fileName) : undefined
+}
+
+export const getContentText = async (content?: ContentYaml) => {
+  const contentFileName = content?.content_path
+  if (!contentFileName) return content?.content
+
+  const contentEntry = await getMarkdownContentByName(contentFileName)
+  return contentEntry?.content
+}
+
+export const loadOverlay = async (
+  injectInfoId: number,
+  overlay?: OverlayYaml
+) => {
+  if (overlay) {
+    await addOverlay({ ...overlay, injectInfoId })
+  }
+}
+
+export const getMilestoneCondition = (
+  condition: string,
+  milestonesWithIds: MappedMilestone[]
+) =>
+  condition
+    .split(' ')
+    .map(
+      value =>
+        Number(
+          ALL_OPERATORS.find(operator => operator.label === value)?.value
+        ) || milestonesWithIds.find(milestone => milestone.name === value)?.id
+    )
+    .filter(id => id !== undefined) as number[]
diff --git a/frontend/src/editor/yaml/parse/tools.tsx b/frontend/src/editor/yaml/parse/tools.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..a18fcd939ee2f4ca3f425977b7eb7ff45e0a7e6b
--- /dev/null
+++ b/frontend/src/editor/yaml/parse/tools.tsx
@@ -0,0 +1,88 @@
+import {
+  addTool,
+  addToolResponse,
+  getMilestoneByTypeAndReferenceId,
+  updateToolResponseMilestoneCondition,
+} from '@/editor/indexeddb/operations'
+import { MilestoneEventType } from '@/editor/indexeddb/types'
+import type {
+  MappedActivity,
+  MappedMilestone,
+  MappedToolResponseControl,
+  ToolResponseYaml,
+  ToolYaml,
+} from '../types'
+import { extractFirstMilestone } from './milestones'
+import { getContentFile, getContentText, getMilestoneCondition } from './shared'
+
+const loadToolResponses = async (
+  toolId: number,
+  responses: ToolResponseYaml[],
+  activityMilestones: MappedActivity[]
+) =>
+  await Promise.all(
+    responses.map(async response => {
+      const file = await getContentFile(response.content)
+      const content = await getContentText(response.content)
+      const learningActivityId = activityMilestones.find(
+        activity =>
+          extractFirstMilestone(response.control?.activate_milestone) ===
+          activity.milestoneName
+      )?.id
+
+      const id = await addToolResponse({
+        toolId,
+        learningActivityId,
+        parameter: response.param,
+        isRegex: response.regex,
+        content,
+        fileId: file?.id,
+        time: response.time,
+      })
+      const milestone = learningActivityId
+        ? await getMilestoneByTypeAndReferenceId(
+            MilestoneEventType.LEARNING_ACTIVITY,
+            learningActivityId
+          )
+        : await getMilestoneByTypeAndReferenceId(MilestoneEventType.TOOL, id)
+
+      return {
+        id,
+        milestoneId: milestone?.id,
+        milestoneName: extractFirstMilestone(
+          response.control?.activate_milestone
+        ),
+        milestoneCondition: response.control?.milestone_condition,
+      }
+    })
+  )
+
+export const loadTools = async (
+  activityMilestones: MappedActivity[],
+  tools: ToolYaml[]
+) =>
+  await Promise.all(
+    tools.map(async tool => {
+      const id = await addTool({
+        name: tool.name,
+        tooltipDescription: tool.tooltip_description,
+        defaultResponse: tool.default_response,
+        hint: tool.hint,
+      })
+      return await loadToolResponses(id, tool.responses, activityMilestones)
+    })
+  )
+
+export const loadToolResponseControls = (
+  toolControls: MappedToolResponseControl[],
+  milestonesWithIds: MappedMilestone[]
+) => {
+  toolControls.forEach(
+    async control =>
+      control.milestoneCondition &&
+      (await updateToolResponseMilestoneCondition(
+        control.id,
+        getMilestoneCondition(control.milestoneCondition, milestonesWithIds)
+      ))
+  )
+}
diff --git a/frontend/src/editor/yaml/types.tsx b/frontend/src/editor/yaml/types.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b8e2a861ab2a3480c82e69ea04e80d33415ab5fd
--- /dev/null
+++ b/frontend/src/editor/yaml/types.tsx
@@ -0,0 +1,176 @@
+import type { LearningActivityType } from '../indexeddb/types'
+
+export type ConfigYaml = {
+  exercise_duration: number
+  email_between_teams?: boolean
+  custom_email_suffix?: string
+  show_exercise_time?: boolean
+  enable_roles?: boolean
+  version: string
+}
+
+export type ChannelYaml = {
+  name: string
+  type: string
+}
+
+export type LearningObjectiveYaml = {
+  name: string
+  tags?: string
+  activities: LearningActivityYaml[]
+}
+
+export type LearningObjectiveYamlWithTypes = {
+  name: string
+  tags?: string
+  activities: LearningActivityYamlWithType[]
+}
+
+export type LearningActivityYaml = {
+  name: string
+  tags?: string
+}
+
+export type LearningActivityYamlWithType = LearningActivityYaml & {
+  type: LearningActivityType
+}
+
+export type ContentYaml = {
+  content?: string
+  content_path?: string
+  file_name?: string
+}
+
+export type ControlYaml = {
+  milestone_condition?: string
+  activate_milestone?: string
+  deactivate_milestone?: string
+  roles?: string
+}
+
+export type OverlayYaml = {
+  duration: number
+}
+
+export type ToolYaml = {
+  name: string
+  tooltip_description?: string
+  hint?: string
+  default_response: string
+  roles?: string
+  responses: ToolResponseYaml[]
+}
+
+export type ToolResponseYaml = {
+  param: string
+  regex?: boolean
+  time?: number
+  content?: ContentYaml
+  control?: ControlYaml
+}
+
+export type EmailAddressYaml = {
+  address: string
+  team_visible?: boolean
+  description: string
+  control?: ControlYaml
+  organization?: string
+  templates?: EmailTemplateYaml[]
+}
+
+export type EmailTemplateYaml = {
+  context: string
+  content?: ContentYaml
+  control?: ControlYaml
+}
+
+export type InjectCategoryYaml = {
+  name: string
+  time?: number
+  delay?: number
+  organization?: string
+  type?: string
+  alternatives: InjectAlternativeInfoYaml[] | InjectAlternativeEmailYaml[]
+}
+
+export type InjectAlternativeInfoYaml = {
+  name: string
+  content?: ContentYaml
+  control?: ControlYaml
+  overlay?: OverlayYaml
+}
+
+export type InjectAlternativeEmailYaml = {
+  name: string
+  sender: string
+  subject: string
+  content?: ContentYaml
+  control?: ControlYaml
+  extra_copies?: number
+  overlay?: OverlayYaml
+}
+
+export type QuestionnaireYaml = {
+  title: string
+  time?: number
+  control?: ControlYaml
+  overlay?: OverlayYaml
+  questions: QuestionYaml[]
+}
+
+export type QuestionYaml = {
+  content: ContentYaml
+  max: number
+  labels?: string
+  correct?: number
+  control?: ControlYaml
+}
+
+export type MilestoneYaml = {
+  name: string
+  roles?: string
+  file_names?: string
+  final?: boolean
+  activity?: string
+  initial_state?: boolean
+}
+
+export type MappedActivity = {
+  id: number
+  milestoneName: string
+}
+
+export type MappedMilestone = {
+  id: number
+  name: string
+}
+
+export type MappedInjectControl = {
+  id: number
+  milestoneId?: number
+  milestoneName?: string
+  milestoneCondition?: string
+  time?: number
+  delay?: number
+}
+
+export type MappedQuestionnaireControl = {
+  id: number
+  milestoneId?: number
+  milestoneName?: string
+  milestoneCondition?: string
+  time?: number
+}
+
+export type MappedToolResponseControl = {
+  id: number
+  milestoneId?: number
+  milestoneName?: string
+  milestoneCondition?: string
+}
+
+export type MappedEmailTemplateControl = {
+  id: number
+  milestoneId?: number
+  milestoneName?: string
+}
diff --git a/frontend/src/editor/downloadDefinitionZip.tsx b/frontend/src/editor/zip/createDefinitionZip.tsx
similarity index 59%
rename from frontend/src/editor/downloadDefinitionZip.tsx
rename to frontend/src/editor/zip/createDefinitionZip.tsx
index b276867755c3ae9143f98eda5544c068a4d235fb..9ce541f0829a5a3bdeb9fca3569e353df7b7f164 100644
--- a/frontend/src/editor/downloadDefinitionZip.tsx
+++ b/frontend/src/editor/zip/createDefinitionZip.tsx
@@ -9,10 +9,22 @@ import { generateQuestionnaires } from '@/editor/yaml/generate/questionnaires'
 import { generateTools } from '@/editor/yaml/generate/tools'
 import JSZip from 'jszip'
 import { stringify } from 'yaml'
-import { InjectType } from './indexeddb/types'
-import type { EditorConfig } from './useEditorStorage'
+import { InjectType } from '../indexeddb/types'
+import type { EditorConfig } from '../useEditorStorage'
+import {
+  CHANNELS_FILE_NAME,
+  CONFIG_FILE_NAME,
+  CONTENT_FOLDER_NAME,
+  EMAIL_FILE_NAME,
+  FILES_FOLDER_NAME,
+  INJECTS_FILE_NAME,
+  MILESTONES_FILE_NAME,
+  OBJECTIVES_FILE_NAME,
+  QUESTIONNAIRES_FILE_NAME,
+  TOOLS_FILE_NAME,
+} from './utils'
 
-export const downloadDefinitionZip = async (config: EditorConfig) => {
+export const createDefinitionZip = async (config: EditorConfig) => {
   const zip = new JSZip()
 
   const generatedChannels = await generateChannels(config)
@@ -21,11 +33,11 @@ export const downloadDefinitionZip = async (config: EditorConfig) => {
   const generatedMilestones = await generateMilestones(config)
   const generatedObjectives = await generateObjectives()
 
-  zip.file('channels.yml', stringify(generatedChannels))
-  zip.file('config.yml', stringify(generatedConfig))
-  zip.file('injects.yml', stringify(generatedInjects))
-  zip.file('milestones.yml', stringify(generatedMilestones))
-  zip.file('objectives.yml', stringify(generatedObjectives))
+  zip.file(CHANNELS_FILE_NAME, stringify(generatedChannels))
+  zip.file(CONFIG_FILE_NAME, stringify(generatedConfig))
+  zip.file(INJECTS_FILE_NAME, stringify(generatedInjects))
+  zip.file(MILESTONES_FILE_NAME, stringify(generatedMilestones))
+  zip.file(OBJECTIVES_FILE_NAME, stringify(generatedObjectives))
 
   const emailAddressesCount = await db.emailAddresses.count()
   const toolsCount = await db.tools.count()
@@ -35,26 +47,36 @@ export const downloadDefinitionZip = async (config: EditorConfig) => {
 
   if (emailAddressesCount) {
     const generatedEmail = await generateEmailAddresses()
-    zip.file('email.yml', stringify(generatedEmail))
+    zip.file(EMAIL_FILE_NAME, stringify(generatedEmail))
   }
   if (questionnairesCount) {
     const generatedQuestionnaires = await generateQuestionnaires()
-    zip.file('questionnaires.yml', stringify(generatedQuestionnaires))
+    zip.file(QUESTIONNAIRES_FILE_NAME, stringify(generatedQuestionnaires))
   }
   if (toolsCount) {
     const generatedTools = await generateTools()
-    zip.file('tools.yml', stringify(generatedTools))
+    zip.file(TOOLS_FILE_NAME, stringify(generatedTools))
   }
 
   const files = await db.files.toArray()
 
   if (files.length) {
-    const filesFolder = zip.folder('files')
+    const filesFolder = zip.folder(FILES_FOLDER_NAME)
     files.forEach(file => {
       filesFolder?.file(file.name, file.blob)
     })
   }
 
+  const markdowns = await db.markdownContents.toArray()
+
+  if (markdowns.length) {
+    const markdownsFolder = zip.folder(CONTENT_FOLDER_NAME)
+    markdowns.forEach(file => {
+      markdownsFolder?.file(file.fileName, file.content)
+    })
+    await db.markdownContents.clear()
+  }
+
   zip.generateAsync({ type: 'blob' }).then(content => {
     const link = document.createElement('a')
     link.href = URL.createObjectURL(content)
diff --git a/frontend/src/editor/zip/loadDefinitionZip.tsx b/frontend/src/editor/zip/loadDefinitionZip.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..5f5ee65451042f9e71b1651b48c6738ffa183d3d
--- /dev/null
+++ b/frontend/src/editor/zip/loadDefinitionZip.tsx
@@ -0,0 +1,167 @@
+import notEmpty from '@inject/shared/utils/notEmpty'
+import JSZip from 'jszip'
+import { parse } from 'yaml'
+import { CONCLUSION_CONDITIONS } from '../assets/pageContent/conclusion'
+import { INTRODUCTION_CONDITIONS } from '../assets/pageContent/introduction'
+import { db } from '../indexeddb/db'
+import { addFile, addMarkdownContent } from '../indexeddb/operations'
+import type { EditorConfig } from '../useEditorStorage'
+import { parseChannels } from '../yaml/parse/channels'
+import { parseConfig } from '../yaml/parse/config'
+import { loadEmailAddresses } from '../yaml/parse/email'
+import { loadInjectControls, loadInjects } from '../yaml/parse/injects'
+import {
+  extractMilestonesFromControls,
+  filterEmailMilestones,
+  filterToolMilestones,
+} from '../yaml/parse/milestones'
+import {
+  addActivityIdsToActivityMilestones,
+  addTypesToActivities,
+  loadObjectives,
+} from '../yaml/parse/objectives'
+import {
+  loadQuestionnaireControls,
+  loadQuestionnaires,
+} from '../yaml/parse/questionnaires'
+import { loadToolResponseControls, loadTools } from '../yaml/parse/tools'
+import type { MilestoneYaml } from '../yaml/types'
+import {
+  CHANNELS_FILE_NAME,
+  CONFIG_FILE_NAME,
+  CONTENT_FOLDER_NAME,
+  EMAIL_FILE_NAME,
+  FILES_FOLDER_NAME,
+  INJECTS_FILE_NAME,
+  MILESTONES_FILE_NAME,
+  OBJECTIVES_FILE_NAME,
+  QUESTIONNAIRES_FILE_NAME,
+  TOOLS_FILE_NAME,
+} from './utils'
+
+const parseZipFile = async (zip: JSZip, fileName: string) => {
+  const file = await zip.file(fileName)?.async('string')
+  return parse(file || '')
+}
+
+export const loadEditorConfig = async (file: File): Promise<EditorConfig> => {
+  const zip = await new JSZip().loadAsync(file)
+
+  const configYaml = await parseZipFile(zip, CONFIG_FILE_NAME)
+  const parsedConfig = parseConfig(configYaml)
+
+  const channelsYaml = await parseZipFile(zip, CHANNELS_FILE_NAME)
+  const parsedChannels = parseChannels(channelsYaml)
+
+  const config = {
+    introChecked: INTRODUCTION_CONDITIONS.map(() => true),
+    conclusionChecked: CONCLUSION_CONDITIONS.map(() => true),
+    name: file.name,
+    description: 'Description',
+    trainee: 'Trainee',
+    ...parsedConfig,
+    ...parsedChannels,
+  }
+
+  return config
+}
+
+const preloadFilesToDb = async (zip: JSZip) => {
+  const filesFolder = zip.folder(FILES_FOLDER_NAME)
+  filesFolder?.forEach(async (path, entry) => {
+    const fileContent = await entry.async('blob')
+    await addFile({ name: path, blob: fileContent })
+  })
+
+  const contentFolder = zip.folder(CONTENT_FOLDER_NAME)
+  contentFolder?.forEach(async (path, entry) => {
+    const fileContent = await entry.async('string')
+    await addMarkdownContent({ fileName: path, content: fileContent })
+  })
+}
+
+export const loadDbData = async (file: File) => {
+  const zip = await new JSZip().loadAsync(file)
+
+  await preloadFilesToDb(zip)
+
+  const emailYaml = await parseZipFile(zip, EMAIL_FILE_NAME)
+  const injectsYaml = await parseZipFile(zip, INJECTS_FILE_NAME)
+  const milestonesYaml = await parseZipFile(zip, MILESTONES_FILE_NAME)
+  const objectivesYaml = await parseZipFile(zip, OBJECTIVES_FILE_NAME)
+  const questionnairesYaml = await parseZipFile(zip, QUESTIONNAIRES_FILE_NAME)
+  const toolsYaml = await parseZipFile(zip, TOOLS_FILE_NAME)
+
+  const activityMilestones = milestonesYaml.filter(
+    (milestone: MilestoneYaml) => milestone.activity
+  )
+
+  // get tool responses that activate LA milestone
+  const toolMilestones = toolsYaml
+    ? filterToolMilestones(activityMilestones, toolsYaml)
+    : []
+  // get email templates that activate LA milestone
+  const emailMilestones = emailYaml
+    ? filterEmailMilestones(activityMilestones, emailYaml)
+    : []
+  // add channel type to activities
+  const objectivesWithActivityTypes = addTypesToActivities(
+    objectivesYaml,
+    toolMilestones,
+    emailMilestones
+  )
+  const activities = (await loadObjectives(objectivesWithActivityTypes)).flat(1)
+
+  // get mapping: definition milestone name -> learning activity id + milestone id
+  const activityMilestonesWithIds = addActivityIdsToActivityMilestones(
+    activityMilestones,
+    activities
+  ).filter(notEmpty)
+
+  /*
+   * when adding email templates: add LA id AND get mapping milestone name - email template id
+   * needs to happen before loading injects, so the email addresses are already stored
+   */
+  const emailControls = emailYaml
+    ? (await loadEmailAddresses(activityMilestonesWithIds, emailYaml)).flat(1)
+    : []
+
+  // when adding tool responses: add LA id AND get mapping milestone name - tool response id + save milestone condition
+  const toolControls = toolsYaml
+    ? (await loadTools(activityMilestonesWithIds, toolsYaml)).flat(1)
+    : []
+
+  // when adding questionnaires: get mapping milestone name - inject id + save milestone condition
+  const questionnaireControls = questionnairesYaml
+    ? await loadQuestionnaires(questionnairesYaml)
+    : []
+
+  // when adding injects: get mapping milestone name - inject id + save milestone condition + group alternatives
+  const injectControlGroups = (await loadInjects(injectsYaml)).filter(notEmpty)
+  const injectControls = injectControlGroups.flat(1)
+
+  // milestone from definition to milestone id
+  const milestonesWithIds = extractMilestonesFromControls(
+    toolControls,
+    emailControls,
+    injectControls,
+    questionnaireControls
+  )
+
+  loadToolResponseControls(toolControls, milestonesWithIds)
+  loadQuestionnaireControls(questionnaireControls, milestonesWithIds)
+  loadInjectControls(injectControlGroups, milestonesWithIds)
+
+  // get final milestone
+  const finalMilestone: MilestoneYaml = milestonesYaml.find(
+    (milestone: MilestoneYaml) => milestone.final
+  )
+  const finalMilestoneId = milestonesWithIds.find(
+    milestone => milestone.name === finalMilestone.name
+  )?.id
+
+  // remove temporary content
+  await db.markdownContents.clear()
+
+  return finalMilestoneId
+}
diff --git a/frontend/src/editor/zip/utils.tsx b/frontend/src/editor/zip/utils.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..859e97c8452d70d820248d8f299aeeb4c1de72ac
--- /dev/null
+++ b/frontend/src/editor/zip/utils.tsx
@@ -0,0 +1,11 @@
+export const CHANNELS_FILE_NAME = 'channels.yml'
+export const CONFIG_FILE_NAME = 'config.yml'
+export const EMAIL_FILE_NAME = 'email.yml'
+export const INJECTS_FILE_NAME = 'injects.yml'
+export const MILESTONES_FILE_NAME = 'milestones.yml'
+export const OBJECTIVES_FILE_NAME = 'objectives.yml'
+export const QUESTIONNAIRES_FILE_NAME = 'questionnaires.yml'
+export const TOOLS_FILE_NAME = 'tools.yml'
+
+export const FILES_FOLDER_NAME = 'files'
+export const CONTENT_FOLDER_NAME = 'content'
diff --git a/frontend/src/pages/editor/create/other.tsx b/frontend/src/pages/editor/create/other.tsx
index 5efc3b510699ff27e7795e3725cb524bbb50ebcd..b171943363d50ed982033ecc775186d70593d61e 100644
--- a/frontend/src/pages/editor/create/other.tsx
+++ b/frontend/src/pages/editor/create/other.tsx
@@ -15,6 +15,7 @@ const OtherPage = () => {
       prevPath='/editor/create/inject-specification'
       nextPath='/editor/create/final-information'
       pageVisible={access?.injectsFilled}
+      nextDisabled={!access?.specificationsFilled}
     >
       <CardList>
         <Card interactive onClick={() => nav(`/editor/create/tools`)}>