diff --git a/frontend/src/editor/InjectForm/index.tsx b/frontend/src/editor/InjectForm/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..fec430db07c85e492508eb6aa161b1de02f2b15c
--- /dev/null
+++ b/frontend/src/editor/InjectForm/index.tsx
@@ -0,0 +1,149 @@
+import type { ButtonProps } from '@blueprintjs/core'
+import {
+  Button,
+  Dialog,
+  DialogBody,
+  DialogFooter,
+  HTMLSelect,
+  InputGroup,
+  Label,
+  TextArea,
+} from '@blueprintjs/core'
+import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
+import type { FC } from 'react'
+import { memo, useCallback, useEffect, useState } from 'react'
+import { addInjectInfo, updateInjectInfo } from '../indexeddb/operations'
+import type { InjectInfo } from '../indexeddb/types'
+import { InjectType } from '../indexeddb/types'
+import { INJECT_TYPES } from '../utils'
+
+interface InjectFormProps {
+  inject?: InjectInfo
+  buttonProps: ButtonProps
+}
+
+const InjectForm: FC<InjectFormProps> = ({ inject, buttonProps }) => {
+  const [isOpen, setIsOpen] = useState(false)
+  const [name, setName] = useState<string>('')
+  const [description, setDescription] = useState<string>('')
+  const [type, setType] = useState<InjectType>(InjectType.INFORMATION)
+
+  const { notify } = useNotifyContext()
+
+  const clearInput = useCallback(() => {
+    setName('')
+    setDescription('')
+  }, [])
+
+  const handleAddButton = useCallback(
+    async (inject: Omit<InjectInfo, 'id'>) => {
+      try {
+        await addInjectInfo(inject)
+        clearInput()
+        setIsOpen(false)
+      } catch (err) {
+        notify(`Failed to add inject '${inject.name}': ${err}`, {
+          intent: 'danger',
+        })
+      }
+    },
+    [notify]
+  )
+
+  const handleUpdateButton = useCallback(
+    async (inject: InjectInfo) => {
+      try {
+        await updateInjectInfo(inject)
+        setIsOpen(false)
+      } catch (err) {
+        notify(`Failed to update inject '${inject.name}': ${err}`, {
+          intent: 'danger',
+        })
+      }
+    },
+    [notify]
+  )
+
+  useEffect(() => {
+    setName(inject?.name || '')
+    setDescription(inject?.description || '')
+    setType(inject?.type || InjectType.INFORMATION)
+  }, [inject])
+
+  return (
+    <>
+      <Button {...buttonProps} onClick={() => setIsOpen(true)} />
+      <Dialog
+        isOpen={isOpen}
+        onClose={() => setIsOpen(false)}
+        icon={inject ? 'edit' : 'plus'}
+        title={inject ? 'Edit inject' : 'New inject'}
+      >
+        <DialogBody>
+          <Label style={{ width: '100%' }}>
+            Title
+            <InputGroup
+              placeholder='Input text'
+              value={name}
+              onChange={e => setName(e.target.value)}
+            />
+          </Label>
+          <Label style={{ width: '100%' }}>
+            What is this inject about? (optional)
+            <TextArea
+              value={description}
+              style={{
+                width: '100%',
+                height: '5rem',
+                resize: 'none',
+                overflowY: 'auto',
+              }}
+              placeholder='Input text'
+              onChange={e => setDescription(e.target.value)}
+            />
+          </Label>
+          <Label style={{ width: '100%' }}>
+            Channel
+            <HTMLSelect
+              options={INJECT_TYPES}
+              value={type}
+              onChange={event =>
+                setType(event.currentTarget.value as InjectType)
+              }
+            />
+          </Label>
+        </DialogBody>
+        <DialogFooter
+          actions={
+            inject ? (
+              <Button
+                disabled={!name}
+                onClick={() =>
+                  handleUpdateButton({
+                    id: inject.id,
+                    name,
+                    description,
+                    type,
+                  })
+                }
+                intent='primary'
+                icon='edit'
+                text='Save changes'
+              />
+            ) : (
+              <Button
+                disabled={!name}
+                onClick={() => handleAddButton({ name, description, type })}
+                intent='primary'
+                icon='plus'
+                text='Add'
+              />
+            )
+          }
+        />
+      </Dialog>
+    </>
+  )
+}
+
+export default memo(InjectForm)
diff --git a/frontend/src/editor/Injects/Inject.tsx b/frontend/src/editor/Injects/Inject.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..9b3e50a586a361e81bab861897dc11cd564fe173
--- /dev/null
+++ b/frontend/src/editor/Injects/Inject.tsx
@@ -0,0 +1,58 @@
+import { Button, ButtonGroup, Card, Icon } from '@blueprintjs/core'
+import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
+import type { FC } from 'react'
+import { memo, useCallback } from 'react'
+import InjectForm from '../InjectForm'
+import { deleteInjectInfo } from '../indexeddb/operations'
+import type { InjectInfo } from '../indexeddb/types'
+import { InjectType } from '../indexeddb/types'
+
+interface InjectItemProps {
+  inject: InjectInfo
+}
+
+const InjectItem: FC<InjectItemProps> = ({ inject }) => {
+  const { notify } = useNotifyContext()
+
+  const handleDeleteButton = useCallback(
+    async (inject: InjectInfo) => {
+      try {
+        await deleteInjectInfo(inject.id)
+      } catch (err) {
+        notify(`Failed to delete inject '${inject.name}': ${err}`, {
+          intent: 'danger',
+        })
+      }
+    },
+    [notify]
+  )
+
+  return (
+    <Card style={{ display: 'flex', justifyContent: 'space-between' }}>
+      <span style={{ height: '100%', flexGrow: 1 }}>
+        <Icon
+          icon={inject.type === InjectType.EMAIL ? 'envelope' : 'clipboard'}
+          style={{ marginRight: '1rem' }}
+        />
+        {inject.name}
+      </span>
+      <ButtonGroup>
+        <InjectForm
+          inject={inject}
+          buttonProps={{
+            minimal: true,
+            icon: 'edit',
+            style: { marginRight: '1rem' },
+          }}
+        />
+        <Button
+          minimal
+          icon='cross'
+          onClick={() => handleDeleteButton(inject)}
+        />
+      </ButtonGroup>
+    </Card>
+  )
+}
+
+export default memo(InjectItem)
diff --git a/frontend/src/editor/Injects/index.tsx b/frontend/src/editor/Injects/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..dac5dd0fa84f4f0217619d78b41f2c0cc010b43f
--- /dev/null
+++ b/frontend/src/editor/Injects/index.tsx
@@ -0,0 +1,20 @@
+import Inject from '@/editor/Injects/Inject'
+import { CardList } from '@blueprintjs/core'
+import { useLiveQuery } from 'dexie-react-hooks'
+import { memo } from 'react'
+import { db } from '../indexeddb/db'
+import type { InjectInfo } from '../indexeddb/types'
+
+const Injects = () => {
+  const injects = useLiveQuery(() => db.injectInfos.toArray(), [], [])
+
+  return (
+    <CardList>
+      {injects?.map((inject: InjectInfo) => (
+        <Inject key={inject.id} inject={inject} />
+      ))}
+    </CardList>
+  )
+}
+
+export default memo(Injects)
diff --git a/frontend/src/editor/indexeddb/db.tsx b/frontend/src/editor/indexeddb/db.tsx
index 6a0c3973f6e38618ef210fcc3d465075db0fc414..be0cb235bb08e746c048c0848ddfb8e8a9003cb5 100644
--- a/frontend/src/editor/indexeddb/db.tsx
+++ b/frontend/src/editor/indexeddb/db.tsx
@@ -1,15 +1,17 @@
 import Dexie, { type EntityTable } from 'dexie'
-import type { LearningObjectiveInfo } from './types'
+import type { InjectInfo, LearningObjectiveInfo } from './types'
 
 const dbName = 'EditorDatabase'
 const dbVersion = 1
 
 const db = new Dexie(dbName) as Dexie & {
   learningObjectives: EntityTable<LearningObjectiveInfo, 'id'>
+  injectInfos: EntityTable<InjectInfo, 'id'>
 }
 
 db.version(dbVersion).stores({
   learningObjectives: '++id, &name',
+  injectInfos: '++id, &name, description, type',
 })
 
 export { db }
diff --git a/frontend/src/editor/indexeddb/operations.tsx b/frontend/src/editor/indexeddb/operations.tsx
index 8ee3e1e1991261223b6b8f7875bdd8510ac4fffc..4ac05600c9b7150da8f2adbac74885bfea3747d5 100644
--- a/frontend/src/editor/indexeddb/operations.tsx
+++ b/frontend/src/editor/indexeddb/operations.tsx
@@ -1,5 +1,5 @@
 import { db } from './db'
-import type { LearningObjectiveInfo } from './types'
+import type { InjectInfo, LearningObjectiveInfo } from './types'
 
 // learning objectives operations
 export const addLearningObjective = async (
@@ -11,3 +11,15 @@ export const addLearningObjective = async (
 
 export const deleteLearningObjective = async (id: string) =>
   await db.learningObjectives.delete(id)
+
+// inject info operations
+export const addInjectInfo = async (injectInfo: Omit<InjectInfo, 'id'>) =>
+  await db.transaction('rw', db.injectInfos, async () => {
+    await db.injectInfos.add(injectInfo)
+  })
+
+export const updateInjectInfo = async (injectInfo: InjectInfo) =>
+  await db.injectInfos.put(injectInfo)
+
+export const deleteInjectInfo = async (id: number) =>
+  await db.injectInfos.delete(id)
diff --git a/frontend/src/editor/indexeddb/types.tsx b/frontend/src/editor/indexeddb/types.tsx
index abc5b0479793e3a87ea9c5ba4cdf5f5cba8a33b8..e5a58b70f2138c79901601197ffd1f712b0145f9 100644
--- a/frontend/src/editor/indexeddb/types.tsx
+++ b/frontend/src/editor/indexeddb/types.tsx
@@ -1,3 +1,15 @@
 import type { LearningObjective } from '@inject/graphql/fragments/LearningObjective.generated'
 
 export type LearningObjectiveInfo = Pick<LearningObjective, 'id' | 'name'>
+
+export enum InjectType {
+  INFORMATION = 'Information',
+  EMAIL = 'Email',
+}
+
+export type InjectInfo = {
+  id: number
+  name: string
+  description: string
+  type: InjectType
+}
diff --git a/frontend/src/editor/utils.tsx b/frontend/src/editor/utils.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..8c9629c81f422db00472059b6764647218b1441c
--- /dev/null
+++ b/frontend/src/editor/utils.tsx
@@ -0,0 +1,3 @@
+import { InjectType } from './indexeddb/types'
+
+export const INJECT_TYPES = Object.values(InjectType)
diff --git a/frontend/src/pages/editor/create/injects.tsx b/frontend/src/pages/editor/create/injects.tsx
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f122c18854d2354cb206fd4f0a107669a4ea4324 100644
--- a/frontend/src/pages/editor/create/injects.tsx
+++ b/frontend/src/pages/editor/create/injects.tsx
@@ -0,0 +1,60 @@
+import InjectForm from '@/editor/InjectForm'
+import Injects from '@/editor/Injects'
+import { useNavigate } from '@/router'
+import { Button } from '@blueprintjs/core'
+import { css } from '@emotion/css'
+import { memo } from 'react'
+
+const injectsPage = css`
+  display: grid;
+  grid-template-rows: auto auto 1fr auto;
+  height: 100vh;
+`
+
+const InjectsPage = () => {
+  const nav = useNavigate()
+
+  return (
+    <div className={injectsPage}>
+      <h1>Define injects</h1>
+      <p style={{ marginBottom: '1rem' }}>Description.</p>
+      <div style={{ overflowY: 'auto' }}>
+        <Injects />
+        <InjectForm
+          buttonProps={{
+            minimal: true,
+            text: 'Add new inject',
+            alignText: 'left',
+            icon: 'plus',
+            style: { padding: '1rem', width: '100%' },
+          }}
+        />
+      </div>
+      <div
+        style={{
+          display: 'flex',
+          justifyContent: 'center',
+          padding: '0.5rem 0',
+        }}
+      >
+        <Button
+          type='button'
+          onClick={() => nav('/editor/create/learning-objectives')}
+          text='Back'
+          icon='arrow-left'
+          style={{
+            marginRight: '0.5rem',
+          }}
+        />
+        <Button
+          type='button'
+          text='Continue'
+          intent='primary'
+          rightIcon='arrow-right'
+        />
+      </div>
+    </div>
+  )
+}
+
+export default memo(InjectsPage)