diff --git a/server/app/apis/competitions.py b/server/app/apis/competitions.py
index db2ca68a44b9d8c88ee0abbf0d4be98c8dc1684d..a18ed85ed92897f016fa44db4f69281b5d749526 100644
--- a/server/app/apis/competitions.py
+++ b/server/app/apis/competitions.py
@@ -61,3 +61,15 @@ class CompetitionSearch(Resource):
         args = competition_search_parser.parse_args(strict=True)
         items, total = dbc.search.competition(**args)
         return list_response(list_schema.dump(items), total)
+
+
+@api.route("/<CID>/copy")
+@api.param("CID")
+class SlidesOrder(Resource):
+    @check_jwt(editor=True)
+    def post(self, CID):
+        item_competition = dbc.get.competition(CID)
+
+        item_competition_copy = dbc.copy.competition(item_competition)
+
+        return item_response(schema.dump(item_competition_copy))
diff --git a/server/app/apis/slides.py b/server/app/apis/slides.py
index 02d0d3d699cf44a29acd5cee7e50fe3d4456c548..3c25f901ba48c86eac2c421da1bd3e659f24350a 100644
--- a/server/app/apis/slides.py
+++ b/server/app/apis/slides.py
@@ -83,3 +83,15 @@ class SlidesOrder(Resource):
         item_slide = dbc.edit.switch_order(item_slide, item_slide_order)
 
         return item_response(schema.dump(item_slide))
+
+
+@api.route("/<SOrder>/copy")
+@api.param("CID,SOrder")
+class SlidesOrder(Resource):
+    @check_jwt(editor=True)
+    def post(self, CID, SOrder):
+        item_slide = dbc.get.slide(CID, SOrder)
+
+        item_slide_copy = dbc.copy.slide(item_slide)
+
+        return item_response(schema.dump(item_slide_copy))
diff --git a/server/app/database/controller/__init__.py b/server/app/database/controller/__init__.py
index 2865b52300df3a80dce6554e9ad018faf5f06edc..a46a65f1e6c2fe4e0664c462288a5f00c1cdd46c 100644
--- a/server/app/database/controller/__init__.py
+++ b/server/app/database/controller/__init__.py
@@ -1,3 +1,3 @@
 # import add, get
 from app.core import db
-from app.database.controller import add, delete, edit, get, search, utils
+from app.database.controller import add, copy, delete, edit, get, search, utils
diff --git a/server/app/database/controller/add.py b/server/app/database/controller/add.py
index de0135f2f514745017b85f654f4afc3203b69975..be7a773e9e579669393dadd83527ad779726e8cc 100644
--- a/server/app/database/controller/add.py
+++ b/server/app/database/controller/add.py
@@ -140,20 +140,36 @@ def slide(item_competition):
 
 
 def competition(name, year, city_id):
-    """ Adds a competition to the database using the provided arguments. """
+    """
+    Adds a competition to the database using the
+    provided arguments. Also adds slide and codes.
+    """
 
-    item_competition = db_add(Competition(name, year, city_id))
+    item_competition = _competition(name, year, city_id)
 
     # Add one slide for the competition
     slide(item_competition)
 
+    # TODO: Add two teams
+
+    return item_competition
+
+
+def _competition(name, year, city_id, font=None):
+    """
+    Internal function. Adds a competition to the database
+    using the provided arguments. Also adds codes.
+    """
+
+    item_competition = db_add(Competition(name, year, city_id))
+    if font:
+        item_competition.font = font
+
     # Add code for Judge view
     code(item_competition.id, 2)
 
     # Add code for Audience view
     code(item_competition.id, 3)
 
-    # TODO: Add two teams
-
     utils.refresh(item_competition)
     return item_competition
diff --git a/server/app/database/controller/copy.py b/server/app/database/controller/copy.py
new file mode 100644
index 0000000000000000000000000000000000000000..dabd0d7807025f9c908a6840e9e0a64c40b64a57
--- /dev/null
+++ b/server/app/database/controller/copy.py
@@ -0,0 +1,108 @@
+"""
+This file contains functionality to copy and duplicate data to the database.
+"""
+
+from app.database.controller import add, get, search, utils
+from app.database.models import Question
+
+
+def _question(item_question_old, slide_id):
+    """
+    Internal function. Makes a copy of the provided question item to the
+    specified slide. Does not copy team, question answers or alternatives.
+    """
+
+    item_question_new = add.db_add(
+        Question(
+            item_question_old.name,
+            item_question_old.total_score,
+            item_question_old.type_id,
+            slide_id,
+        )
+    )
+
+    # TODO: Add question alternatives
+    # for item_alternatives in item_question_old.alternatives:
+    #     dbc.add.alternatives()
+
+    return item_question_new
+
+
+def _component(item_component, item_slide_new):
+    """
+    Internal function. Makes a copy of the provided
+    component item to the specified slide.
+    """
+
+    add.component(
+        item_component.type_id,
+        item_slide_new,
+        item_component.data,
+        item_component.x,
+        item_component.y,
+        item_component.w,
+        item_component.h,
+    )
+
+
+def slide(item_slide_old):
+    """
+    Deep copies a slide to the same competition.
+    Does not copy team, question answers or alternatives.
+    """
+
+    item_competition = get.competition(item_slide_old.competition_id)
+
+    return slide_to_competition(item_slide_old, item_competition)
+
+
+def slide_to_competition(item_slide_old, item_competition):
+    """
+    Deep copies a slide to the provided competition.
+    Does not copy team, question answers or alternatives.
+    """
+
+    item_slide_new = add.slide(item_competition)
+
+    # Copy all fields
+    item_slide_new.title = item_slide_old.title
+    item_slide_new.body = item_slide_old.body
+    item_slide_new.timer = item_slide_old.timer
+    item_slide_new.settings = item_slide_old.settings
+
+    # TODO: Add background image
+
+    for item_component in item_slide_old.components:
+        _component(item_component, item_slide_new)
+
+    for item_question in item_slide_old.questions:
+        _question(item_question, item_slide_new.id)
+
+    utils.commit_and_refresh(item_slide_new)
+    return item_slide_new
+
+
+def competition(item_competition_old):
+    """
+    Adds a deep-copy of the provided competition.
+    Will not copy teams, question answers or alternatives.
+    """
+
+    name = "Kopia av " + item_competition_old.name
+    item_competition, total = search.competition(name=name)
+    if item_competition:
+        print(f"{item_competition[total-1].name}, {total=}")
+        name = "Kopia av " + item_competition[total - 1].name
+
+    item_competition_new = add._competition(
+        name,
+        item_competition_old.year,
+        item_competition_old.city_id,
+        item_competition_old.font,
+    )
+    # TODO: Add background image
+
+    for item_slide in item_competition_old.slides:
+        slide_to_competition(item_slide, item_competition_new)
+
+    return item_competition_new
diff --git a/server/tests/test_app.py b/server/tests/test_app.py
index 467fd0596269bbb875262c9b1be74f45807ed494..cc50d4fd857082671158a3a4467d8adbb7e9914f 100644
--- a/server/tests/test_app.py
+++ b/server/tests/test_app.py
@@ -98,6 +98,16 @@ def test_competition_api(client):
     response, body = delete(client, f"/api/competitions/{competition_id}", headers=headers)
     assert response.status_code == codes.OK
 
+    # Get competition
+    competition_id = 2
+    response, body = get(client, f"/api/competitions/{competition_id}", headers=headers)
+    assert response.status_code == codes.OK
+
+    # Copies competition
+    for _ in range(10):
+        response, _ = post(client, f"/api/competitions/{competition_id}/copy", headers=headers)
+        assert response.status_code == codes.OK
+
 
 def test_auth_and_user_api(client):
     add_default_values()
@@ -301,6 +311,11 @@ def test_slide_api(client):
     # Changes the order
     change_order_test(client, CID, slide_order, slide_order + 1, headers)
 
+    # Copies slide
+    for _ in range(10):
+        response, _ = post(client, f"/api/competitions/{CID}/slides/{slide_order}/copy", headers=headers)
+        assert response.status_code == codes.OK
+
 
 def test_question_api(client):
     add_default_values()
diff --git a/server/tests/test_db.py b/server/tests/test_db.py
index 3bb998aec9674b9ab0cc865ec2e9c22a9640e73a..7d162065c8857a24a137b1f55c610f969045fd60 100644
--- a/server/tests/test_db.py
+++ b/server/tests/test_db.py
@@ -1,5 +1,5 @@
 import app.database.controller as dbc
-from app.database.models import City, Competition, Media, MediaType, Question, QuestionType, Role, Slide, Team, User
+from app.database.models import City, Media, MediaType, Role, User
 
 from tests import app, client, db
 from tests.test_helpers import add_default_values, assert_exists, assert_insert_fail
@@ -40,6 +40,80 @@ def test_media(client):
     assert item_media.upload_by.email == "test@test.se"
 
 
+def test_copy(client):
+    add_default_values()
+
+    # Fetches an empty competition
+    list_item_competitions, _ = dbc.search.competition(name="Tävling 1")
+    item_competition_original = list_item_competitions[0]
+
+    # Fetches the first slide in that competition
+    num_slides = 3
+    item_slides, total = dbc.search.slide(competition_id=item_competition_original.id)
+    assert total == num_slides
+    item_slide_original = item_slides[0]
+
+    # Inserts several copies of the same slide
+    num_copies = 10
+    for _ in range(num_copies):
+        item_slide_copy = dbc.copy.slide(item_slide_original)
+        num_slides += 1
+        check_slides_copy(item_slide_original, item_slide_copy, num_slides, num_slides - 1)
+        assert item_slide_copy.competition_id == item_slide_original.competition_id
+
+    # Copies competition
+    num_copies = 3
+    for _ in range(num_copies):
+        item_competition_copy = dbc.copy.competition(item_competition_original)
+        for order, item_slide in enumerate(item_competition_copy.slides):
+            item_slide_original = item_competition_original.slides[order]
+            check_slides_copy(item_slide_original, item_slide, num_slides, order)
+            assert item_slide.competition_id != item_slide_original.competition_id
+
+
+def check_slides_copy(item_slide_original, item_slide_copy, num_slides, order):
+    """ Checks that two slides are correct copies of each other. Looks big but is quite fast. """
+    assert item_slide_copy.order == order  # 0 indexing
+    assert item_slide_copy.title == item_slide_original.title
+    assert item_slide_copy.body == item_slide_original.body
+    assert item_slide_copy.timer == item_slide_original.timer
+    assert item_slide_copy.settings == item_slide_original.settings
+
+    # Checks that all components were correctly copied
+    assert len(item_slide_copy.components) == len(item_slide_original.components)
+    for i, c1 in enumerate(item_slide_original.components):
+        c2 = item_slide_copy.components[i]
+        assert c1 != c2
+        assert c1.x == c2.x
+        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
+
+    # Checks that all questions were correctly copied
+    assert len(item_slide_copy.questions) == len(item_slide_original.questions)
+    for i, q1 in enumerate(item_slide_original.questions):
+        q2 = item_slide_copy.questions[i]
+        assert q1 != q2
+        assert q1.name == q2.name
+        assert q1.total_score == q2.total_score
+        assert q1.type_id == q2.type_id
+        assert q1.slide_id == item_slide_original.id
+        assert q2.slide_id == item_slide_copy.id
+        # TODO: Assert alternatives
+
+    # Checks that the copy put the slide in the database
+    item_slides, total = dbc.search.slide(
+        competition_id=item_slide_copy.competition_id,
+        # page_size=num_slides + 1, # Use this total > 15
+    )
+    assert total == num_slides
+    assert item_slide_copy == item_slides[order]
+
+
 """
 def test_question(client):
     add_default_values()
diff --git a/server/tests/test_helpers.py b/server/tests/test_helpers.py
index fbd77d9ef146676c0cb6af94581c984b730ac3db..7a68655b5a0212012736b40627050dab81f86595 100644
--- a/server/tests/test_helpers.py
+++ b/server/tests/test_helpers.py
@@ -47,17 +47,19 @@ def add_default_values():
         dbc.add.slide(item_comp)
 
         # Add slides
-        i = 1
-        for item_slide in item_comp.slides:
+        for i, item_slide in enumerate(item_comp.slides):
             # Populate slide with data
-            item_slide.title = f"Title {i}"
-            item_slide.body = f"Body {i}"
-            item_slide.timer = 100 + i
+            item_slide.title = f"Title {i+1}"
+            item_slide.body = f"Body {i+1}"
+            item_slide.timer = 100 + i + 1
             # item_slide.settings = "{}"
             dbc.utils.commit_and_refresh(item_slide)
+
             # Add question to competition
-            dbc.add.question(name=f"Q{i}", total_score=i, type_id=1, item_slide=item_slide)
-            i += 1
+            dbc.add.question(name=f"Q{i+1}", total_score=i + 1, type_id=1, item_slide=item_slide)
+
+            # Add text component
+            dbc.add.component(1, item_slide, {"text": "Text"}, i, 2 * i, 3 * i, 4 * i)
 
 
 def get_body(response):