diff --git a/client/src/interfaces/ApiModels.ts b/client/src/interfaces/ApiModels.ts
index 85933175e45980d46acf8f62dc0af55ca7e3ee23..73fe6db65f1b733abd4d0c7c7e81f1e74f907e19 100644
--- a/client/src/interfaces/ApiModels.ts
+++ b/client/src/interfaces/ApiModels.ts
@@ -83,17 +83,13 @@ export interface Component {
 }
 
 export interface ImageComponent extends Component {
-  data: {
-    media_id: number
-    filename: string
-  }
+  media_id: number
+  filename: string
 }
 
 export interface TextComponent extends Component {
-  data: {
-    text: string
-    font: string
-  }
+  text: string
+  font: string
 }
 
 export interface QuestionAlternativeComponent extends Component {
diff --git a/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx b/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx
index 7886a9b15899dfe4b790be9d3673591b4d1940d7..5e409d57f0e6f8a7503745760dd9078440fa732c 100644
--- a/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx
+++ b/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx
@@ -10,7 +10,7 @@ type ImageComponentProps = {
 const ImageComponentDisplay = ({ component, width, height }: ImageComponentProps) => {
   return (
     <img
-      src={`http://localhost:5000/static/images/${component.data.filename}`}
+      src={`http://localhost:5000/static/images/${component.filename}`}
       height={height}
       width={width}
       draggable={false}
diff --git a/client/src/pages/presentationEditor/components/Images.tsx b/client/src/pages/presentationEditor/components/Images.tsx
index 4f942374a604dbee5b8796c645b19f1f545e6c56..b715ba2c4482d6d074f53409fbb1f4abab996409 100644
--- a/client/src/pages/presentationEditor/components/Images.tsx
+++ b/client/src/pages/presentationEditor/components/Images.tsx
@@ -46,10 +46,7 @@ const Images = ({ activeSlide, competitionId }: ImagesProps) => {
     const imageData = {
       x: 0,
       y: 0,
-      data: {
-        media_id: media.id,
-        filename: media.filename,
-      },
+      media_id: media.id,
       type_id: 2,
     }
     await axios
@@ -78,7 +75,7 @@ const Images = ({ activeSlide, competitionId }: ImagesProps) => {
   const handleCloseimageClick = async (image: ImageComponent) => {
     // Removes selected image component and deletes its file from the server.
     await axios
-      .delete(`/api/media/images/${image.data.media_id}`)
+      .delete(`/api/media/images/${image.media_id}`)
       .then(() => {
         dispatch(getEditorCompetition(competitionId))
       })
@@ -112,9 +109,9 @@ const Images = ({ activeSlide, competitionId }: ImagesProps) => {
           images.map((image) => (
             <div key={image.id}>
               <ListItem divider button>
-                <ImportedImage src={`http://localhost:5000/static/images/thumbnail_${image.data.filename}`} />
+                <ImportedImage src={`http://localhost:5000/static/images/thumbnail_${image.filename}`} />
                 <Center>
-                  <ListItemText primary={image.data.filename} />
+                  <ListItemText primary={image.filename} />
                 </Center>
                 <CloseIcon onClick={() => handleCloseimageClick(image)} />
               </ListItem>
diff --git a/client/src/pages/presentationEditor/components/RndComponent.tsx b/client/src/pages/presentationEditor/components/RndComponent.tsx
index effd3362df2cbb5ae8301c7e731934f6dee7abb6..c7b5933abb8144722d4348a81de59f1fd669002e 100644
--- a/client/src/pages/presentationEditor/components/RndComponent.tsx
+++ b/client/src/pages/presentationEditor/components/RndComponent.tsx
@@ -72,9 +72,7 @@ const RndComponent = ({ component, width, height }: ImageComponentProps) => {
           <HoverContainer
             hover={hover}
             dangerouslySetInnerHTML={{
-              __html: `<div style="font-size: ${Math.round(24 * scale)}px;">${
-                (component as TextComponent).data.text
-              }</div>`,
+              __html: `<div style="font-size: ${Math.round(24 * scale)}px;">${(component as TextComponent).text}</div>`,
             }}
           />
         )
@@ -83,7 +81,7 @@ const RndComponent = ({ component, width, height }: ImageComponentProps) => {
           <HoverContainer hover={hover}>
             <img
               key={component.id}
-              src={`/static/images/${(component as ImageComponent).data.filename}`}
+              src={`/static/images/${(component as ImageComponent).filename}`}
               height={currentSize.h * scale}
               width={currentSize.w * scale}
               draggable={false}
@@ -95,7 +93,7 @@ const RndComponent = ({ component, width, height }: ImageComponentProps) => {
           <HoverContainer hover={hover}>
             <img
               key={component.id}
-              src={`/static/images/${(component as ImageComponent).data.filename}`}
+              src={`/static/images/${(component as ImageComponent).filename}`}
               height={currentSize.h * scale}
               width={currentSize.w * scale}
               draggable={false}
diff --git a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
index 0a5c8522447c3111629ac179ce27532048d55c7a..f50e547a8d1e7f5eadadca1ad1d531a46cc0410e 100644
--- a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
+++ b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
@@ -24,7 +24,7 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => {
   const dispatch = useAppDispatch()
 
   useEffect(() => {
-    setContent(component.data.text)
+    setContent(component.text)
   }, [])
 
   const handleSaveText = async (a: string) => {
@@ -38,7 +38,7 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => {
       window.setTimeout(async () => {
         console.log('Content was updated on server. id: ', component.id)
         await axios.put(`/api/competitions/${competitionId}/slides/${activeSlideId}/components/${component.id}`, {
-          data: { ...component.data, text: a },
+          data: { ...component, text: a },
         })
         dispatch(getEditorCompetition(id))
       }, 250)
diff --git a/client/src/pages/presentationEditor/components/Texts.tsx b/client/src/pages/presentationEditor/components/Texts.tsx
index 22cde214c92bb64788b4f69d9993977c85fe981d..31ecf57cd29325972328647290fc74de7c9827bd 100644
--- a/client/src/pages/presentationEditor/components/Texts.tsx
+++ b/client/src/pages/presentationEditor/components/Texts.tsx
@@ -27,7 +27,7 @@ const Texts = ({ activeSlide, competitionId }: TextsProps) => {
     if (activeSlide) {
       await axios.post(`/api/competitions/${competitionId}/slides/${activeSlide?.id}/components`, {
         type_id: 1,
-        data: { text: 'Ny text' },
+        text: 'Ny text',
         w: 315,
         h: 50,
       })
diff --git a/server/app/apis/media.py b/server/app/apis/media.py
index 830b16de0a6125dde7c95406c8b7bc928c2bc105..8f0b28f7b332184f043f3f79562c02b6a001f7ca 100644
--- a/server/app/apis/media.py
+++ b/server/app/apis/media.py
@@ -1,41 +1,21 @@
-import os
-
 import app.core.http_codes as codes
 import app.database.controller as dbc
 from app.apis import check_jwt, item_response, list_response
 from app.core.dto import MediaDTO
 from app.core.parsers import media_parser_search
 from app.database.models import City, Media, MediaType, QuestionType, Role
-from flask import current_app, request
+from flask import request
 from flask_jwt_extended import get_jwt_identity, jwt_required
 from flask_restx import Resource, reqparse
 from flask_uploads import UploadNotAllowed
-from PIL import Image
 from sqlalchemy import exc
+import app.core.files as files
 
 api = MediaDTO.api
 image_set = MediaDTO.image_set
 schema = MediaDTO.schema
 list_schema = MediaDTO.list_schema
 
-PHOTO_PATH = current_app.config["UPLOADED_PHOTOS_DEST"]
-
-
-def generate_thumbnail(filename):
-    thumbnail_size = current_app.config["THUMBNAIL_SIZE"]
-    path = os.path.join(PHOTO_PATH, filename)
-    thumb_path = os.path.join(PHOTO_PATH, f"thumbnail_{filename}")
-    with Image.open(path) as im:
-        im.thumbnail(thumbnail_size)
-        im.save(thumb_path)
-
-
-def delete_image(filename):
-    path = os.path.join(PHOTO_PATH, filename)
-    thumb_path = os.path.join(PHOTO_PATH, f"thumbnail_{filename}")
-    os.remove(path)
-    os.remove(thumb_path)
-
 
 @api.route("/images")
 class ImageList(Resource):
@@ -50,16 +30,16 @@ class ImageList(Resource):
         if "image" not in request.files:
             api.abort(codes.BAD_REQUEST, "Missing image in request.files")
         try:
-            filename = image_set.save(request.files["image"])
-            generate_thumbnail(filename)
-            print(filename)
-            item = dbc.add.image(filename, get_jwt_identity())
+            filename = files.save_image_with_thumbnail(request.files["image"])
+            item = Media.query.filter(Media.filename == filename).first()
+            if not item:
+                item = dbc.add.image(filename, get_jwt_identity())
+
+            return item_response(schema.dump(item))
         except UploadNotAllowed:
             api.abort(codes.BAD_REQUEST, "Could not save the image")
         except:
             api.abort(codes.INTERNAL_SERVER_ERROR, "Something went wrong when trying to save image")
-        finally:
-            return item_response(schema.dump(item))
 
 
 @api.route("/images/<ID>")
@@ -74,11 +54,10 @@ class ImageList(Resource):
     def delete(self, ID):
         item = dbc.get.one(Media, ID)
         try:
-            delete_image(item.filename)
+            files.delete_image_and_thumbnail(item.filename)
             dbc.delete.default(item)
+            return {}, codes.NO_CONTENT
         except OSError:
             api.abort(codes.BAD_REQUEST, "Could not delete the file image")
         except exc.SQLAlchemyError:
             api.abort(codes.INTERNAL_SERVER_ERROR, "Something went wrong when trying to delete image")
-        finally:
-            return {}, codes.NO_CONTENT
diff --git a/server/app/core/files.py b/server/app/core/files.py
new file mode 100644
index 0000000000000000000000000000000000000000..01a03166811b02f06e2b21f21430dd25837b2067
--- /dev/null
+++ b/server/app/core/files.py
@@ -0,0 +1,88 @@
+from PIL import Image, ImageChops
+from flask import current_app
+import os
+import datetime
+from flask_uploads import IMAGES, UploadSet
+
+PHOTO_PATH = current_app.config["UPLOADED_PHOTOS_DEST"]
+THUMBNAIL_SIZE = current_app.config["THUMBNAIL_SIZE"]
+image_set = UploadSet("photos", IMAGES)
+
+
+def compare_images(input_image, output_image):
+    # compare image dimensions (assumption 1)
+    if input_image.size != output_image.size:
+        return False
+
+    rows, cols = input_image.size
+
+    # compare image pixels (assumption 2 and 3)
+    for row in range(rows):
+        for col in range(cols):
+            input_pixel = input_image.getpixel((row, col))
+            output_pixel = output_image.getpixel((row, col))
+            if input_pixel != output_pixel:
+                return False
+
+    return True
+
+
+def _delete_image(filename):
+    path = os.path.join(PHOTO_PATH, filename)
+    os.remove(path)
+
+
+def save_image_with_thumbnail(image_file):
+    saved_filename = image_set.save(image_file)
+    saved_path = os.path.join(PHOTO_PATH, saved_filename)
+    with Image.open(saved_path) as im:
+        im_thumbnail = im.copy()
+        im_thumbnail.thumbnail(THUMBNAIL_SIZE)
+        thumb_path = os.path.join(PHOTO_PATH, f"thumbnail_{saved_filename}")
+        im_thumbnail.save(thumb_path)
+        im.close()
+        return saved_filename
+
+
+def delete_image_and_thumbnail(filename):
+    _delete_image(filename)
+    _delete_image(f"thumbnail_{filename}")
+
+
+"""
+def _resolve_name_conflict(filename):
+    split = os.path.splitext(filename)
+    suffix = split[0]
+    preffix = split[1]
+    now = datetime.datetime.now()
+    time_stamp = now.strftime("%Y%m%d%H%M%S")
+    return f"{suffix}-{time_stamp}{preffix}"
+"""
+"""
+def save_image_with_thumbnail(image_file):
+    filename = image_file.filename
+    path = os.path.join(PHOTO_PATH, filename)
+
+    saved_filename = image_set.save(image_file)
+    saved_path = os.path.join(PHOTO_PATH, saved_filename)
+    im = Image.open(saved_path)
+
+    # Check if image already exists
+    if path != saved_path:
+        im_existing = Image.open(path)
+        # If both images are identical, then return None
+        if compare_images(im, im_existing):
+            im.close()
+            im_existing.close()
+            _delete_image(saved_filename)
+            return filename
+
+    path = os.path.join(PHOTO_PATH, saved_filename)
+    im_thumbnail = im.copy()
+    im_thumbnail.thumbnail(THUMBNAIL_SIZE)
+
+    thumb_path = os.path.join(PHOTO_PATH, f"thumbnail_{saved_filename}")
+    im_thumbnail.save(thumb_path)
+    im.close()
+    return saved_filename
+"""
diff --git a/server/app/core/parsers.py b/server/app/core/parsers.py
index 71f8fdee352c3fe88831124b0c8907fcbc51f505..1e9813e99d9d131d42dd221ef893ff6eb99b81f6 100644
--- a/server/app/core/parsers.py
+++ b/server/app/core/parsers.py
@@ -97,11 +97,13 @@ component_parser.add_argument("x", type=str, default=None, location="json")
 component_parser.add_argument("y", type=int, default=None, location="json")
 component_parser.add_argument("w", type=int, default=None, location="json")
 component_parser.add_argument("h", type=int, default=None, location="json")
-component_parser.add_argument("data", type=dict, default=None, location="json")
+# component_parser.add_argument("data", type=dict, default=None, location="json")
 
 component_create_parser = component_parser.copy()
-component_create_parser.replace_argument("data", type=dict, required=True, location="json")
+# component_create_parser.replace_argument("data", type=dict, required=True, location="json")
 component_create_parser.add_argument("type_id", type=int, required=True, location="json")
+component_create_parser.add_argument("text", type=str, required=False, location="json")
+component_create_parser.add_argument("media_id", type=str, required=False, location="json")
 
 login_code_parser = reqparse.RequestParser()
 login_code_parser.add_argument("code", type=str, location="json")
diff --git a/server/app/core/rich_schemas.py b/server/app/core/rich_schemas.py
index 8852c392c4a060a065f618a66393e55b8a2627a8..da5f58d653ea5efb6c00648db56c1bc81a24d44b 100644
--- a/server/app/core/rich_schemas.py
+++ b/server/app/core/rich_schemas.py
@@ -42,6 +42,8 @@ class SlideSchemaRich(RichSchema):
     title = ma.auto_field()
     timer = ma.auto_field()
     competition_id = ma.auto_field()
+    background_image_id = ma.auto_field()
+    background_image = ma.Function(lambda x: x.background_image.filename if x.background_image is not None else "")
     questions = fields.Nested(QuestionSchemaRich, many=True)
     components = fields.Nested(schemas.ComponentSchema, many=True)
 
@@ -54,6 +56,9 @@ class CompetitionSchemaRich(RichSchema):
     name = ma.auto_field()
     year = ma.auto_field()
     city_id = ma.auto_field()
+    background_image_id = ma.auto_field()
+    background_image = ma.Function(lambda x: x.background_image.filename if x.background_image is not None else "")
+
     slides = fields.Nested(
         SlideSchemaRich,
         many=True,
diff --git a/server/app/core/schemas.py b/server/app/core/schemas.py
index 378f99af09167fc7ea6d7b40798d355cff5e8514..4e440fc4b5e282201f19602a428a506e763f670a 100644
--- a/server/app/core/schemas.py
+++ b/server/app/core/schemas.py
@@ -1,3 +1,4 @@
+from marshmallow.decorators import pre_load
 from marshmallow.decorators import pre_dump
 import app.database.models as models
 from app.core import ma
@@ -115,6 +116,8 @@ class SlideSchema(BaseSchema):
     title = ma.auto_field()
     timer = ma.auto_field()
     competition_id = ma.auto_field()
+    background_image_id = ma.auto_field()
+    background_image = ma.Function(lambda x: x.background_image.filename if x.background_image is not None else "")
 
 
 class TeamSchema(BaseSchema):
@@ -145,6 +148,8 @@ class CompetitionSchema(BaseSchema):
     name = ma.auto_field()
     year = ma.auto_field()
     city_id = ma.auto_field()
+    background_image_id = ma.auto_field()
+    background_image = ma.Function(lambda x: x.background_image.filename if x.background_image is not None else "")
 
 
 class ComponentSchema(BaseSchema):
@@ -156,6 +161,9 @@ class ComponentSchema(BaseSchema):
     y = ma.auto_field()
     w = ma.auto_field()
     h = ma.auto_field()
-    data = ma.Function(lambda obj: obj.data)
     slide_id = ma.auto_field()
     type_id = ma.auto_field()
+
+    text = fields.fields.String()
+    media_id = fields.fields.Integer()
+    filename = ma.Function(lambda x: x.media.filename if hasattr(x, "media_id") else "")
diff --git a/server/app/database/__init__.py b/server/app/database/__init__.py
index 784c006b8fab59ca1dcc9e6dbac8ebffe91cb013..e0c78ad3af3f3376fdecdae4eae219aa1c62be24 100644
--- a/server/app/database/__init__.py
+++ b/server/app/database/__init__.py
@@ -6,6 +6,7 @@ from flask_sqlalchemy.model import Model
 from sqlalchemy import Column, DateTime, Text
 from sqlalchemy.sql import func
 from sqlalchemy.types import TypeDecorator
+from sqlalchemy import event
 
 
 class Base(Model):
diff --git a/server/app/database/controller/add.py b/server/app/database/controller/add.py
index 1f57f7d06c74d9a0fd9999c99a5108b1d59ad25f..dceccfe85851a9dffe24032c0df5cc199f056ab0 100644
--- a/server/app/database/controller/add.py
+++ b/server/app/database/controller/add.py
@@ -14,6 +14,7 @@ from app.database.models import (
     Competition,
     Component,
     ComponentType,
+    ImageComponent,
     Media,
     MediaType,
     Question,
@@ -23,6 +24,7 @@ from app.database.models import (
     Role,
     Slide,
     Team,
+    TextComponent,
     User,
     ViewType,
 )
@@ -30,8 +32,10 @@ from flask.globals import current_app
 from flask_restx import abort
 from PIL import Image
 from sqlalchemy import exc
+from sqlalchemy.orm import with_polymorphic
 from sqlalchemy.orm import relation
 from sqlalchemy.orm.session import sessionmaker
+from flask import current_app
 
 
 def db_add(item):
@@ -55,59 +59,16 @@ def db_add(item):
     return item
 
 
-def blacklist(jti):
-    """ Adds a blacklist to the database. """
-
-    return db_add(Blacklist(jti))
-
-
-def mediaType(name):
-    """ Adds a media type to the database. """
-
-    return db_add(MediaType(name))
-
-
-def questionType(name):
-    """ Adds a question type to the database. """
-
-    return db_add(QuestionType(name))
-
-
-def componentType(name):
-    """ Adds a component type to the database. """
-
-    return db_add(ComponentType(name))
-
-
-def viewType(name):
-    """ Adds a view type to the database. """
-
-    return db_add(ViewType(name))
-
-
-def role(name):
-    """ Adds a role to the database. """
-
-    return db_add(Role(name))
-
-
-def city(name):
-    """ Adds a city to the database. """
-
-    return db_add(City(name))
-
-
-def component(type_id, slide_id, data, x=0, y=0, w=0, h=0):
+def component(type_id, slide_id, x=0, y=0, w=0, h=0, **data):
     """
     Adds a component to the slide at the specified coordinates with the
     provided size and data .
     """
-    from app.apis.media import PHOTO_PATH
 
     if type_id == 2:  # 2 is image
         item_image = get.one(Media, data["media_id"])
         filename = item_image.filename
-        path = os.path.join(PHOTO_PATH, filename)
+        path = os.path.join(current_app.config["UPLOADED_PHOTOS_DEST"], filename)
         with Image.open(path) as im:
             h = im.height
             w = im.width
@@ -118,37 +79,17 @@ def component(type_id, slide_id, data, x=0, y=0, w=0, h=0):
         w *= ratio
         h *= ratio
 
-    return db_add(Component(slide_id, type_id, data, x, y, w, h))
-
-
-def image(filename, user_id):
-    """
-    Adds an image to the database and keeps track of who called the function.
-    """
-
-    return db_add(Media(filename, 1, user_id))
-
-
-def user(email, password, role_id, city_id, name=None):
-    """ Adds a user to the database using the provided arguments. """
-
-    return db_add(User(email, password, role_id, city_id, name))
-
-
-def question(name, total_score, type_id, slide_id):
-    """
-    Adds a question to the specified slide using the provided arguments.
-    """
-
-    return db_add(Question(name, total_score, type_id, slide_id))
-
-
-def question_alternative(text, value, question_id):
-    return db_add(QuestionAlternative(text, value, question_id))
+    if type_id == 1:
+        item = db_add(TextComponent(slide_id, type_id, x, y, w, h))
+        item.text = data.get("text")
+    elif type_id == 2:
+        item = db_add(ImageComponent(slide_id, type_id, x, y, w, h))
+        item.media_id = data.get("media_id")
+    else:
+        abort(codes.BAD_REQUEST, f"Invalid type_id{type_id}")
 
-
-def question_answer(data, score, question_id, team_id):
-    return db_add(QuestionAnswer(data, score, question_id, team_id))
+    item = utils.commit_and_refresh(item)
+    return item
 
 
 def code(view_type_id, competition_id=None, team_id=None):
@@ -235,3 +176,75 @@ def _competition_no_slides(name, year, city_id, font=None):
 
     item_competition = utils.refresh(item_competition)
     return item_competition
+
+
+def blacklist(jti):
+    """ Adds a blacklist to the database. """
+
+    return db_add(Blacklist(jti))
+
+
+def mediaType(name):
+    """ Adds a media type to the database. """
+
+    return db_add(MediaType(name))
+
+
+def questionType(name):
+    """ Adds a question type to the database. """
+
+    return db_add(QuestionType(name))
+
+
+def componentType(name):
+    """ Adds a component type to the database. """
+
+    return db_add(ComponentType(name))
+
+
+def viewType(name):
+    """ Adds a view type to the database. """
+
+    return db_add(ViewType(name))
+
+
+def role(name):
+    """ Adds a role to the database. """
+
+    return db_add(Role(name))
+
+
+def city(name):
+    """ Adds a city to the database. """
+
+    return db_add(City(name))
+
+
+def image(filename, user_id):
+    """
+    Adds an image to the database and keeps track of who called the function.
+    """
+
+    return db_add(Media(filename, 1, user_id))
+
+
+def user(email, password, role_id, city_id, name=None):
+    """ Adds a user to the database using the provided arguments. """
+
+    return db_add(User(email, password, role_id, city_id, name))
+
+
+def question(name, total_score, type_id, slide_id):
+    """
+    Adds a question to the specified slide using the provided arguments.
+    """
+
+    return db_add(Question(name, total_score, type_id, slide_id))
+
+
+def question_alternative(text, value, question_id):
+    return db_add(QuestionAlternative(text, value, question_id))
+
+
+def question_answer(data, score, question_id, team_id):
+    return db_add(QuestionAnswer(data, score, question_id, team_id))
diff --git a/server/app/database/controller/copy.py b/server/app/database/controller/copy.py
index 79a4df0f4a7f308b7bc40b5b9ab20e61423ffb14..87e3f831542aac9478566e916d12118f66a7c120 100644
--- a/server/app/database/controller/copy.py
+++ b/server/app/database/controller/copy.py
@@ -37,15 +37,19 @@ def _component(item_component, item_slide_new):
     Internal function. Makes a copy of the provided
     component item to the specified slide.
     """
-
+    data = {}
+    if item_component.type_id == 1:
+        data["text"] = item_component.text
+    elif item_component.type_id == 2:
+        data["media_id"] = item_component.media_id
     add.component(
         item_component.type_id,
         item_slide_new.id,
-        item_component.data,
         item_component.x,
         item_component.y,
         item_component.w,
         item_component.h,
+        **data,
     )
 
 
diff --git a/server/app/database/controller/get.py b/server/app/database/controller/get.py
index 3e5e7f872c2866c70131dd1b4f1205c3bd131798..ba975d35432437746de480667bb8774267cc5437 100644
--- a/server/app/database/controller/get.py
+++ b/server/app/database/controller/get.py
@@ -2,17 +2,20 @@
 This file contains functionality to get data from the database.
 """
 
+from sqlalchemy.orm.util import with_polymorphic
 from app.core import db
 from app.core import http_codes as codes
 from app.database.models import (
     Code,
     Competition,
     Component,
+    ImageComponent,
     Question,
     QuestionAlternative,
     QuestionAnswer,
     Slide,
     Team,
+    TextComponent,
     User,
 )
 from sqlalchemy.orm import joinedload, subqueryload
@@ -216,7 +219,15 @@ def component(competition_id, slide_id, component_id):
     join_competition = Competition.id == Slide.competition_id
     join_slide = Slide.id == Component.slide_id
     filters = (Competition.id == competition_id) & (Slide.id == slide_id) & (Component.id == component_id)
-    return Component.query.join(Competition, join_competition).join(Slide, join_slide).filter(filters).first_extended()
+
+    poly = with_polymorphic(Component, [TextComponent, ImageComponent])
+    return (
+        db.session.query(poly)
+        .join(Competition, join_competition)
+        .join(Slide, join_slide)
+        .filter(filters)
+        .first_extended()
+    )
 
 
 def component_list(competition_id, slide_id):
diff --git a/server/app/database/models.py b/server/app/database/models.py
index 7174080b799eeea7d5956ca641c0c8fdca8d5ee2..1ed99d911700c64349816e937230f2cd0e90e437 100644
--- a/server/app/database/models.py
+++ b/server/app/database/models.py
@@ -2,6 +2,8 @@ from app.core import bcrypt, db
 from app.database import Dictionary
 from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property
 
+from app.database.types import ID_IMAGE_COMPONENT, ID_TEXT_COMPONENT
+
 STRING_SIZE = 254
 
 
@@ -91,7 +93,9 @@ class Competition(db.Model):
     year = db.Column(db.Integer, nullable=False, default=2020)
     font = db.Column(db.String(STRING_SIZE), nullable=False)
     city_id = db.Column(db.Integer, db.ForeignKey("city.id"), nullable=False)
+
     background_image_id = db.Column(db.Integer, db.ForeignKey("media.id"), nullable=True)
+    background_image = db.relationship("Media", uselist=False)
 
     slides = db.relationship("Slide", backref="competition")
     teams = db.relationship("Team", backref="competition")
@@ -190,20 +194,36 @@ class Component(db.Model):
     y = db.Column(db.Integer, nullable=False, default=0)
     w = db.Column(db.Integer, nullable=False, default=1)
     h = db.Column(db.Integer, nullable=False, default=1)
-    data = db.Column(Dictionary())
+    view_type_id = db.Column(db.Integer, db.ForeignKey("view_type.id"), nullable=True)
     slide_id = db.Column(db.Integer, db.ForeignKey("slide.id"), nullable=False)
     type_id = db.Column(db.Integer, db.ForeignKey("component_type.id"), nullable=False)
 
-    def __init__(self, slide_id, type_id, data, x=0, y=0, w=1, h=1):
+    __mapper_args__ = {"polymorphic_on": type_id}
+
+    def __init__(self, slide_id, type_id, x=0, y=0, w=1, h=1):
         self.x = x
         self.y = y
         self.w = w
         self.h = h
-        self.data = data
         self.slide_id = slide_id
         self.type_id = type_id
 
 
+class TextComponent(Component):
+    text = db.Column(db.Text, default="", nullable=False)
+
+    # __tablename__ = None
+    __mapper_args__ = {"polymorphic_identity": ID_TEXT_COMPONENT}
+
+
+class ImageComponent(Component):
+    media_id = db.Column(db.Integer, db.ForeignKey("media.id"), nullable=True)
+    media = db.relationship("Media", uselist=False)
+
+    # __tablename__ = None
+    __mapper_args__ = {"polymorphic_identity": ID_IMAGE_COMPONENT}
+
+
 class Code(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     code = db.Column(db.Text, unique=True)
diff --git a/server/app/database/types.py b/server/app/database/types.py
new file mode 100644
index 0000000000000000000000000000000000000000..236e50f5278d03684f2cbdcc05c2e7637e21a057
--- /dev/null
+++ b/server/app/database/types.py
@@ -0,0 +1,2 @@
+ID_TEXT_COMPONENT = 1
+ID_IMAGE_COMPONENT = 2
diff --git a/server/configmodule.py b/server/configmodule.py
index e202abd3fd4f76899598d77df66de8bf148925a2..8c07211ed58d7fa2550893fc241a9a2170527a2f 100644
--- a/server/configmodule.py
+++ b/server/configmodule.py
@@ -13,7 +13,7 @@ class Config:
     JWT_BLACKLIST_TOKEN_CHECKS = ["access", "refresh"]
     JWT_ACCESS_TOKEN_EXPIRES = timedelta(days=2)
     JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30)
-    UPLOADED_PHOTOS_DEST = os.path.join(os.getcwd(), "app/static/images")
+    UPLOADED_PHOTOS_DEST = os.path.join(os.getcwd(), "app", "static", "images")
     THUMBNAIL_SIZE = (120, 120)
     SECRET_KEY = os.urandom(24)
     SQLALCHEMY_ECHO = False
diff --git a/server/populate.py b/server/populate.py
index cc19aa4cdd43190ac52c302f3029ade08b2a6c7a..6a4be4ef42cbe14d938466282c6db2addb92dc57 100644
--- a/server/populate.py
+++ b/server/populate.py
@@ -86,7 +86,7 @@ def _add_items():
                 y = random.randrange(1, 500)
                 w = random.randrange(150, 400)
                 h = random.randrange(150, 400)
-                dbc.add.component(1, item_slide.id, {"text": f"hej{k}"}, x, y, w, h)
+                dbc.add.component(1, item_slide.id, x, y, w, h, text=f"hej{k}")
 
         # item_slide = dbc.add.slide(item_comp)
         # item_slide.title = f"Slide {len(item_comp.slides)}"
diff --git a/server/tests/test_db.py b/server/tests/test_db.py
index cf10329ce6a320fdceb89ae9eba82da9e6b17092..dd13427c1c62689a88edefc96a6c16b8aa9db682 100644
--- a/server/tests/test_db.py
+++ b/server/tests/test_db.py
@@ -94,11 +94,13 @@ def check_slides_copy(item_slide_original, item_slide_copy, num_slides, order):
         assert c1.y == c2.y
         assert c1.w == c2.w
         assert c1.h == c2.h
-        assert c1.data == c2.data
         assert c1.slide_id == item_slide_original.id
         assert c2.slide_id == item_slide_copy.id
         assert c1.type_id == c2.type_id
-
+        if c1.type_id == 1:
+            assert c1.text == c2.text
+        elif c1.type_id == 2:
+            assert c1.image_id == c2.image_id
     # Checks that all questions were correctly copied
     questions = item_slide_original.questions
     questions_copy = item_slide_copy.questions
diff --git a/server/tests/test_helpers.py b/server/tests/test_helpers.py
index cc630626822aa3358da79b7d9a9a132705982bdf..b5f1e54e136b759d9924a534acf2f37e6f7b08cd 100644
--- a/server/tests/test_helpers.py
+++ b/server/tests/test_helpers.py
@@ -59,7 +59,7 @@ def add_default_values():
             # dbc.add.question(name=f"Q{i+1}", total_score=i + 1, type_id=1, slide_id=item_slide.id)
 
             # Add text component
-            dbc.add.component(1, item_slide.id, {"text": "Text"}, i, 2 * i, 3 * i, 4 * i)
+            dbc.add.component(1, item_slide.id, i, 2 * i, 3 * i, 4 * i, text="Text")
 
 
 def get_body(response):