diff --git a/server/app/apis/__init__.py b/server/app/apis/__init__.py
index c5395c7cddb2256b4b209f7a139c2f09ce4a5b4d..0ab7ce7db5f18c04dc2b5ee1b5a5402387727dd6 100644
--- a/server/app/apis/__init__.py
+++ b/server/app/apis/__init__.py
@@ -115,12 +115,6 @@ def protect_route(allowed_roles=None, allowed_views=None):
 
 # flask_api = Api()
 
-# flask_api.add_namespace(
-#     alternative_ns, path="/api/competitions/<competition_id>/slides/<slide_id>/questions/<question_id>/alternatives"
-# )
-
-# flask_api.add_namespace(component_ns, path="/api/competitions/<competition_id>/slides/<slide_id>/components")
-
 # flask_api.add_namespace(answer_ns, path="/api/competitions/<competition_id>/teams/<team_id>/answers")
 
 # flask_api.add_namespace(score_ns, path="/api/competitions/<competition_id>/teams/<team_id>/answers/question_scores")
@@ -136,6 +130,7 @@ def init_api():
     from .auth import blp as auth_blp
     from .codes import blp as code_blp
     from .competitions import blp as competition_blp
+    from .components import blp as component_blp
     from .media import blp as media_blp
     from .misc import blp as misc_blp
     from .questions import blp as question_blp
@@ -153,3 +148,4 @@ def init_api():
     flask_api.register_blueprint(team_blp)
     flask_api.register_blueprint(code_blp)
     flask_api.register_blueprint(alternative_blp)
+    flask_api.register_blueprint(component_blp)
diff --git a/server/app/apis/components.py b/server/app/apis/components.py
index 39e1932824f6e6ab103f773402bb1c3feb331e43..d0e45da6020652c0e627f17f01d5b5e7785d878d 100644
--- a/server/app/apis/components.py
+++ b/server/app/apis/components.py
@@ -3,97 +3,103 @@ All API calls concerning competitions.
 Default route: /api/competitions/<competition_id>/slides/<slide_id>/components
 """
 
-import app.core.http_codes as codes
 import app.database.controller as dbc
-from app.apis import item_response, list_response, protect_route
-from app.core.dto import ComponentDTO
-from app.core.parsers import sentinel
-from flask_restx import Resource, reqparse
-
-api = ComponentDTO.api
-schema = ComponentDTO.schema
-list_schema = ComponentDTO.list_schema
-
-component_parser_add = reqparse.RequestParser()
-component_parser_add.add_argument("x", type=int, default=0, location="json")
-component_parser_add.add_argument("y", type=int, default=0, location="json")
-component_parser_add.add_argument("w", type=int, default=1, location="json")
-component_parser_add.add_argument("h", type=int, default=1, location="json")
-component_parser_add.add_argument("type_id", type=int, required=True, location="json")
-component_parser_add.add_argument("view_type_id", type=int, required=True, location="json")
-component_parser_add.add_argument("text", type=str, default=None, location="json")
-component_parser_add.add_argument("media_id", type=int, default=None, location="json")
-component_parser_add.add_argument("question_id", type=int, default=None, location="json")
-
-component_parser_edit = reqparse.RequestParser()
-component_parser_edit.add_argument("x", type=int, default=sentinel, location="json")
-component_parser_edit.add_argument("y", type=int, default=sentinel, location="json")
-component_parser_edit.add_argument("w", type=int, default=sentinel, location="json")
-component_parser_edit.add_argument("h", type=int, default=sentinel, location="json")
-component_parser_edit.add_argument("text", type=str, default=sentinel, location="json")
-component_parser_edit.add_argument("media_id", type=int, default=sentinel, location="json")
-component_parser_edit.add_argument("question_id", type=int, default=sentinel, location="json")
-
-
-@api.route("")
-@api.param("competition_id, slide_id")
-class ComponentList(Resource):
+from app.apis import protect_route
+from app.core import ma
+from app.core.schemas import BaseSchema, ComponentSchema
+from app.database import models
+from flask.views import MethodView
+from flask_smorest import Blueprint, abort
+from marshmallow import fields
+
+from . import http_codes
+
+blp = Blueprint(
+    "component",
+    "component",
+    url_prefix="/api/competitions/<competition_id>/slides/<slide_id>/components",
+    description="Adding, updating, deleting and copy components",
+)
+
+
+class ComponentAddArgsSchema(BaseSchema):
+
+    x = fields.Integer(required=False, missing=0)
+    y = fields.Integer(required=False, missing=0)
+    w = fields.Integer(required=False, missing=1)
+    h = fields.Integer(required=False, missing=1)
+
+    type_id = fields.Integer(required=True)
+    view_type_id = fields.Integer(required=True)
+
+    text = fields.String(required=False)
+    media_id = fields.Integer(required=False)
+    question_id = fields.Integer(required=False)
+
+
+class ComponentEditArgsSchema(BaseSchema):
+
+    x = fields.Integer(required=False)
+    y = fields.Integer(required=False)
+    w = fields.Integer(required=False)
+    h = fields.Integer(required=False)
+
+    type_id = fields.Integer(required=False)
+    view_type_id = fields.Integer(required=False)
+
+    text = fields.String(required=False)
+    media_id = fields.Integer(required=False)
+    question_id = fields.Integer(required=False)
+
+
+@blp.route("")
+class Components(MethodView):
     @protect_route(allowed_roles=["*"], allowed_views=["*"])
+    @blp.response(http_codes.OK, ComponentSchema(many=True))
     def get(self, competition_id, slide_id):
         """ Gets all components in the specified slide and competition. """
-
-        items = dbc.get.component_list(competition_id, slide_id)
-        return list_response(list_schema.dump(items))
+        return dbc.get.component_list(competition_id, slide_id)
 
     @protect_route(allowed_roles=["*"])
-    def post(self, competition_id, slide_id):
+    @blp.arguments(ComponentAddArgsSchema)
+    @blp.response(http_codes.OK, ComponentSchema)
+    def post(self, args, competition_id, slide_id):
         """ Posts a new component to the specified slide. """
+        return dbc.add.component(slide_id=slide_id, **args)
 
-        args = component_parser_add.parse_args()
-        item = dbc.add.component(slide_id=slide_id, **args)
-        return item_response(schema.dump(item))
 
-
-@api.route("/<component_id>")
-@api.param("competition_id, slide_id, component_id")
-class ComponentByID(Resource):
+@blp.route("/<component_id>")
+class ComponentById(MethodView):
     @protect_route(allowed_roles=["*"], allowed_views=["*"])
+    @blp.response(http_codes.OK, ComponentSchema)
+    @blp.alt_response(http_codes.NOT_FOUND, None, description="Could not find component")
     def get(self, competition_id, slide_id, component_id):
         """ Gets the specified component. """
-
-        item = dbc.get.component(competition_id, slide_id, component_id)
-        return item_response(schema.dump(item))
+        return dbc.get.component(competition_id, slide_id, component_id)
 
     @protect_route(allowed_roles=["*"])
-    def put(self, competition_id, slide_id, component_id):
+    @blp.arguments(ComponentEditArgsSchema)
+    @blp.response(http_codes.OK, ComponentSchema)
+    @blp.alt_response(http_codes.NOT_FOUND, None, description="Could not find component")
+    @blp.alt_response(http_codes.CONFLICT, None, description="Could not update component with given values")
+    def put(self, args, competition_id, slide_id, component_id):
         """ Edits the specified component using the provided arguments. """
-
-        args = component_parser_edit.parse_args(strict=True)
-        item = dbc.get.component(competition_id, slide_id, component_id)
-        args_without_sentinel = {key: value for key, value in args.items() if value is not sentinel}
-        item = dbc.edit.default(item, **args_without_sentinel)
-        return item_response(schema.dump(item))
+        return dbc.edit.default(dbc.get.component(competition_id, slide_id, component_id), **args)
 
     @protect_route(allowed_roles=["*"])
+    @blp.response(http_codes.NO_CONTENT, None)
+    @blp.alt_response(http_codes.NOT_FOUND, None, description="Could not find component")
+    @blp.alt_response(http_codes.CONFLICT, None, description="Could not delete component")
     def delete(self, competition_id, slide_id, component_id):
         """ Deletes the specified component. """
+        dbc.delete.component(dbc.get.component(competition_id, slide_id, component_id))
+        return None
 
-        item = dbc.get.component(competition_id, slide_id, component_id)
-        dbc.delete.component(item)
-        return {}, codes.NO_CONTENT
 
-
-@api.route("/<component_id>/copy/<view_type_id>")
-@api.param("competition_id, slide_id, component_id, view_type_id")
-class ComponentList(Resource):
+@blp.route("/<component_id>/copy/<view_type_id>")
+class ComponentCopy(MethodView):
     @protect_route(allowed_roles=["*"])
+    @blp.response(http_codes.OK, ComponentSchema)
     def post(self, competition_id, slide_id, component_id, view_type_id):
         """ Creates a deep copy of the specified component. """
-
-        item_component = dbc.get.component(
-            competition_id,
-            slide_id,
-            component_id,
-        )
-        item = dbc.copy.component(item_component, slide_id, view_type_id)
-        return item_response(schema.dump(item))
+        return dbc.copy.component(dbc.get.component(competition_id, slide_id, component_id), slide_id, view_type_id)