diff --git a/app/SpaCyNLPPipelineModel.defaults.yml b/app/SpaCyNLPPipelineModel.defaults.yml
index 576f85e460d8e4b9770781b157dc527f9584dccc..ed4ea3bd854fcf96a0473f1197d487d737a3386e 100644
--- a/app/SpaCyNLPPipelineModel.defaults.yml
+++ b/app/SpaCyNLPPipelineModel.defaults.yml
@@ -5,6 +5,62 @@
   publisher_url: 'https://github.com/explosion'
   publishing_url: 'https://github.com/explosion/spacy-models/releases/tag/de_core_news_md-3.4.0'
   publishing_year: 2022
+  pipeline_name: 'de_core_news_md'
   version: '3.4.0'
   compatible_service_versions:
     - '0.1.0'
+- title: 'en_core_web_md-3.4.1'
+  description: 'English pipeline optimized for CPU. Components: tok2vec, tagger, parser, senter, ner, attribute_ruler, lemmatizer.'
+  url: 'https://github.com/explosion/spacy-models/releases/download/en_core_web_md-3.4.1/en_core_web_md-3.4.1.tar.gz'
+  publisher: 'Explosion'
+  publisher_url: 'https://github.com/explosion'
+  publishing_url: 'https://github.com/explosion/spacy-models/releases/tag/en_core_web_md-3.4.1'
+  publishing_year: 2022
+  pipeline_name: 'en_core_web_md'
+  version: '3.4.1'
+  compatible_service_versions:
+    - '0.1.0'
+- title: 'uk_core_news_md-3.4.0'
+  description: 'Ukrainian pipeline optimized for CPU. Components: tok2vec, morphologizer, parser, senter, ner, attribute_ruler, lemmatizer.'
+  url: 'https://github.com/explosion/spacy-models/releases/download/uk_core_news_md-3.4.0/uk_core_news_md-3.4.0.tar.gz'
+  publisher: 'Explosion'
+  publisher_url: 'https://github.com/explosion'
+  publishing_url: 'https://github.com/explosion/spacy-models/releases/tag/uk_core_news_md-3.4.0'
+  publishing_year: 2022
+  pipeline_name: 'uk_core_news_md'  
+  version: '3.4.0'
+  compatible_service_versions:
+    - '0.1.0'
+- title: 'zh_core_web_md-3.4.0'
+  description: 'Chinese pipeline optimized for CPU. Components: tok2vec, tagger, parser, senter, ner, attribute_ruler.'
+  url: 'https://github.com/explosion/spacy-models/releases/download/zh_core_web_md-3.4.0/zh_core_web_md-3.4.0.tar.gz'
+  publisher: 'Explosion'
+  publisher_url: 'https://github.com/explosion'
+  publishing_url: 'https://github.com/explosion/spacy-models/releases/tag/zh_core_web_md-3.4.0'
+  publishing_year: 2022
+  pipeline_name: 'zh_core_web_md'
+  version: '3.4.0'
+  compatible_service_versions:
+    - '0.1.0'
+- title: 'ru_core_news_md-3.4.0'
+  description: 'Russian pipeline optimized for CPU. Components: tok2vec, morphologizer, parser, senter, ner, attribute_ruler, lemmatizer.'
+  url: 'https://github.com/explosion/spacy-models/releases/download/ru_core_news_md-3.4.0/ru_core_news_md-3.4.0.tar.gz'
+  publisher: 'Explosion'
+  publisher_url: 'https://github.com/explosion'
+  publishing_url: 'https://github.com/explosion/spacy-models/releases/tag/ru_core_news_md-3.4.0'
+  publishing_year: 2022
+  pipeline_name: 'ru_core_news_md'
+  version: '3.4.0'
+  compatible_service_versions:
+    - '0.1.0'
+- title: 'la_core_cltk_sm-0.1.0'
+  description: 'Latin pipeline optimized for CPU. Components: tok2vec, morphologizer, parser, senter, ner, attribute_ruler, lemmatizer.'
+  url: 'https://github.com/diyclassics/latin-spacy-models/raw/main/la_core_cltk_sm/la_core_cltk_sm-0.1.0.tar.gz'
+  publisher: 'DIY Classics'
+  publisher_url: 'https://github.com/diyclassics/'
+  publishing_url: 'https://github.com/diyclassics/latin-spacy-models/tree/main/la_core_cltk_sm'
+  publishing_year: 2022
+  pipeline_name: 'la_core_cltk_sm'
+  version: '0.1.0'
+  compatible_service_versions:
+    - '0.1.0'
diff --git a/app/contributions/forms.py b/app/contributions/forms.py
index 8577ee979b6583c53a9a1b0115430ba93ebf144a..dcdfaea85a8694057ed4c6276485a57187b4d902 100644
--- a/app/contributions/forms.py
+++ b/app/contributions/forms.py
@@ -46,6 +46,18 @@ class CreateContributionBaseForm(FlaskForm):
     )
     submit = SubmitField()
 
+class EditForm(CreateContributionBaseForm):
+    def prefill(self, model_file):
+        ''' Pre-fill the form with data of an exististing corpus file '''
+        self.title.data = model_file.title
+        self.description.data = model_file.description
+        self.publisher.data = model_file.publisher
+        self.publishing_year.data = model_file.publishing_year
+        self.publisher_url.data = model_file.publisher_url
+        self.publishing_url.data = model_file.publishing_url
+        self.version.data = model_file.version
+        self.shared.data = model_file.shared
+
 class TesseractOCRModelContributionForm(CreateContributionBaseForm):
     tesseract_model_file = FileField(
         'File',
@@ -67,16 +79,23 @@ class TesseractOCRModelContributionForm(CreateContributionBaseForm):
         ]
         self.compatible_service_versions.default = ''
 
-class TesseractOCRModelEditForm(CreateContributionBaseForm):
-    def prefill(self, model_file):
-        ''' Pre-fill the form with data of an exististing corpus file '''
-        self.title.data = model_file.title
-        self.description.data = model_file.description
-        self.publisher.data = model_file.publisher
-        self.publishing_year.data = model_file.publishing_year
-        self.publisher_url.data = model_file.publisher_url
-        self.publishing_url.data = model_file.publishing_url
-        self.version.data = model_file.version
-        self.shared.data = model_file.shared
-    
+class SpacyNLPModelContributionForm(CreateContributionBaseForm):
+    spacy_model_file = FileField(
+        'File',
+        validators=[FileRequired()]
+    )
+    compatible_service_versions = SelectMultipleField(
+        'Compatible service versions'
+    )
+    def validate_spacy(self, field):
+        if field.data.mimetype != '.tar.gz':
+            raise ValidationError('.tar.gz files only!')
 
+    def __init__(self, *args, **kwargs):
+        service_manifest = SERVICES['spacy-nlp-pipeline']
+        super().__init__(*args, **kwargs)
+        self.compatible_service_versions.choices = [('', 'Choose your option')]
+        self.compatible_service_versions.choices += [
+            (x, x) for x in service_manifest['versions'].keys()
+        ]
+        self.compatible_service_versions.default = ''
diff --git a/app/contributions/routes.py b/app/contributions/routes.py
index 385e1eec028c6a70f32ffae4baefca4835d22b0d..b63a43ec9c88b38977205bffc0ce637ff0cb6ae1 100644
--- a/app/contributions/routes.py
+++ b/app/contributions/routes.py
@@ -1,11 +1,11 @@
-from flask import abort, current_app, flash, Markup, redirect, render_template, url_for
+from flask import abort, current_app, flash, Markup, render_template, url_for
 from flask_login import login_required, current_user
 from threading import Thread
 from app import db
 from app.decorators import admin_required, permission_required 
-from app.models import TesseractOCRPipelineModel, Permission
+from app.models import Permission, SpaCyNLPPipelineModel, TesseractOCRPipelineModel
 from . import bp
-from .forms import TesseractOCRModelContributionForm, TesseractOCRModelEditForm
+from .forms import TesseractOCRModelContributionForm, EditForm, SpacyNLPModelContributionForm
 
 
 @bp.before_request
@@ -22,20 +22,26 @@ def contributions():
     tesseract_ocr_user_models = [
         x for x in current_user.tesseract_ocr_pipeline_models
     ]
+    spacy_nlp_user_models = [
+        x for x in current_user.spacy_nlp_pipeline_models
+    ]
+    spacy_models = SpaCyNLPPipelineModel.query.all()
+    print(spacy_models)
     return render_template(
         'contributions/contribution_overview.html.j2',
-        tesseractOCRUserModels=tesseract_ocr_user_models,
+        tesseract_ocr_user_models=tesseract_ocr_user_models,
+        spacy_nlp_user_models=spacy_nlp_user_models,
         userId = current_user.hashid,
         title='Contribution Overview'
     )
 
-@bp.route('/<hashid:tesseract_ocr_pipeline_model_id>', methods=['GET', 'POST'])
+@bp.route('/edit-tesseract-model/<hashid:tesseract_ocr_pipeline_model_id>', methods=['GET', 'POST'])
 @login_required
 def tesseract_ocr_pipeline_model(tesseract_ocr_pipeline_model_id):
     tesseract_ocr_pipeline_model = TesseractOCRPipelineModel.query.get_or_404(
         tesseract_ocr_pipeline_model_id
     )
-    form = TesseractOCRModelEditForm(prefix='tesseract-ocr-model-edit-form')
+    form = EditForm(prefix='tesseract-ocr-model-edit-form')
     if form.validate_on_submit():
         if tesseract_ocr_pipeline_model.title != form.title.data:
             tesseract_ocr_pipeline_model.title = form.title.data
@@ -65,7 +71,7 @@ def tesseract_ocr_pipeline_model(tesseract_ocr_pipeline_model_id):
         title='Edit your Tesseract OCR model'
     )
 
-@bp.route('/<hashid:tesseract_ocr_pipeline_model_id>', methods=['DELETE'])
+@bp.route('/edit-tesseract-model/<hashid:tesseract_ocr_pipeline_model_id>', methods=['DELETE'])
 @login_required
 def delete_tesseract_model(tesseract_ocr_pipeline_model_id):
     def _delete_tesseract_model(app, tesseract_ocr_pipeline_model_id):
@@ -123,3 +129,95 @@ def add_tesseract_ocr_pipeline_model():
         tesseract_ocr_pipeline_models=tesseract_ocr_pipeline_models,
         title='Tesseract OCR Model Contribution'
     )
+
+@bp.route('/edit-spacy-model//<hashid:spacy_nlp_pipeline_model_id>', methods=['GET', 'POST'])
+@login_required
+def spacy_nlp_pipeline_model(spacy_nlp_pipeline_model_id):
+    spacy_nlp_pipeline_model = SpaCyNLPPipelineModel.query.get_or_404(
+        spacy_nlp_pipeline_model_id
+    )
+    form = EditForm(prefix='spacy-nlp-model-edit-form')
+    if form.validate_on_submit():
+        if spacy_nlp_pipeline_model.title != form.title.data:
+            spacy_nlp_pipeline_model.title = form.title.data
+        if spacy_nlp_pipeline_model.description != form.description.data:
+            spacy_nlp_pipeline_model.description = form.description.data
+        if spacy_nlp_pipeline_model.publisher != form.publisher.data:
+            spacy_nlp_pipeline_model.publisher = form.publisher.data
+        if spacy_nlp_pipeline_model.publishing_year != form.publishing_year.data:
+            spacy_nlp_pipeline_model.publishing_year = form.publishing_year.data
+        if spacy_nlp_pipeline_model.publisher_url != form.publisher_url.data:
+            spacy_nlp_pipeline_model.publisher_url = form.publisher_url.data
+        if spacy_nlp_pipeline_model.publishing_url != form.publishing_url.data:
+            spacy_nlp_pipeline_model.publishing_url = form.publishing_url.data
+        if spacy_nlp_pipeline_model.version != form.version.data:
+            spacy_nlp_pipeline_model.version = form.version.data
+        if spacy_nlp_pipeline_model.shared != form.shared.data:
+            spacy_nlp_pipeline_model.shared = form.shared.data
+        db.session.commit()
+        message = Markup(f'Model "<a href="contribute/{spacy_nlp_pipeline_model.hashid}">{spacy_nlp_pipeline_model.title}</a>" updated')
+        flash(message, category='corpus')
+        return {}, 201, {'Location': url_for('contributions.contributions')}
+    form.prefill(spacy_nlp_pipeline_model)
+    return render_template(
+        'contributions/spacy_nlp_pipeline_model.html.j2',
+        spacy_nlp_pipeline_model=spacy_nlp_pipeline_model,
+        form=form,
+        title='Edit your spaCy NLP model'
+    )
+
+@bp.route('/edit-spacy-model/<hashid:spacy_nlp_pipeline_model_id>', methods=['DELETE'])
+@login_required
+def delete_spacy_model(spacy_nlp_pipeline_model_id):
+    def _delete_spacy_model(app, spacy_nlp_pipeline_model_id):
+        with app.app_context():
+            model = SpaCyNLPPipelineModel.query.get(spacy_nlp_pipeline_model_id)
+            model.delete()
+            db.session.commit()
+    
+    model = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id)
+    if not (model.user == current_user or current_user.is_administrator()):
+        abort(403)
+    thread = Thread(
+        target=_delete_spacy_model,
+        args=(current_app._get_current_object(), spacy_nlp_pipeline_model_id)
+    )
+    thread.start()
+    return {}, 202
+
+@bp.route('/add-spacy-nlp-pipeline-model', methods=['GET', 'POST'])
+def add_spacy_nlp_pipeline_model():
+    form = SpacyNLPModelContributionForm(prefix='contribute-spacy-nlp-pipeline-model-form')
+    if form.is_submitted():
+        if not form.validate():
+            response = {'errors': form.errors}
+            return response, 400
+        try:
+            spacy_nlp_model = SpaCyNLPPipelineModel.create(
+                form.spacy_model_file.data,
+                compatible_service_versions=form.compatible_service_versions.data,
+                description=form.description.data,
+                publisher=form.publisher.data,
+                publisher_url=form.publisher_url.data,
+                publishing_url=form.publishing_url.data,
+                publishing_year=form.publishing_year.data,
+                shared=form.shared.data,
+                title=form.title.data,
+                version=form.version.data,
+                user=current_user
+            )
+        except OSError:
+            abort(500)
+        db.session.commit()
+        message = Markup(f'Model "{spacy_nlp_model.title}" created')
+        flash(message)
+        return {}, 201, {'Location': url_for('contributions.contributions')}
+    spacy_nlp_pipeline_models = [
+        x for x in SpaCyNLPPipelineModel.query.all()
+    ]
+    return render_template(
+        'contributions/contribute_spacy_nlp_models.html.j2',
+        form=form,
+        spacy_nlp_pipeline_models=spacy_nlp_pipeline_models,
+        title='spaCy NLP Model Contribution'
+    )
diff --git a/app/models.py b/app/models.py
index e1acf6de0b3d74175c67031f8a94e55d77b8158d..90d16f485372bc264e53937356d6392751c00e33 100644
--- a/app/models.py
+++ b/app/models.py
@@ -520,6 +520,10 @@ class User(HashidMixin, UserMixin, db.Model):
                 x.hashid: x.to_json(relationships=True)
                 for x in self.tesseract_ocr_pipeline_models
             }
+            _json['spacy_nlp_pipeline_models'] = {
+                x.hashid: x.to_json(relationships=True)
+                for x in self.spacy_nlp_pipeline_models
+            }
         return _json
 
 class TesseractOCRPipelineModel(FileMixin, HashidMixin, db.Model):
@@ -643,6 +647,7 @@ class SpaCyNLPPipelineModel(FileMixin, HashidMixin, db.Model):
     publisher_url = db.Column(db.String(512))
     publishing_url = db.Column(db.String(512))
     publishing_year = db.Column(db.Integer)
+    pipeline_name = db.Column(db.String(64))
     shared = db.Column(db.Boolean, default=False)
     # Backrefs: user: User
 
@@ -675,6 +680,7 @@ class SpaCyNLPPipelineModel(FileMixin, HashidMixin, db.Model):
                 model.shared = True
                 model.title = m['title']
                 model.version = m['version']
+                model.pipeline_name = m['pipeline_name']
                 continue
             model = SpaCyNLPPipelineModel(
                 compatible_service_versions=m['compatible_service_versions'],
@@ -686,7 +692,8 @@ class SpaCyNLPPipelineModel(FileMixin, HashidMixin, db.Model):
                 shared=True,
                 title=m['title'],
                 user=nopaque_user,
-                version=m['version']
+                version=m['version'],
+                pipeline_name=m['pipeline_name']
             )
             db.session.add(model)
             db.session.flush(objects=[model])
@@ -708,6 +715,13 @@ class SpaCyNLPPipelineModel(FileMixin, HashidMixin, db.Model):
                         f.write(chunk)
                 pbar.close()
         db.session.commit()
+    
+    def delete(self):
+        try:
+            os.remove(self.path)
+        except OSError as e:
+            current_app.logger.error(e)
+        db.session.delete(self)
 
     def to_json(self, backrefs=False, relationships=False):
         _json = {
@@ -718,6 +732,7 @@ class SpaCyNLPPipelineModel(FileMixin, HashidMixin, db.Model):
             'publisher_url': self.publisher_url,
             'publishing_url': self.publishing_url,
             'publishing_year': self.publishing_year,
+            'pipeline_name': self.pipeline_name,
             'shared': self.shared,
             'title': self.title,
             **self.file_mixin_to_json()
diff --git a/app/static/js/RessourceLists/SpacyNLPModelList.js b/app/static/js/RessourceLists/SpacyNLPModelList.js
new file mode 100644
index 0000000000000000000000000000000000000000..0e20191b0d35694a2d378ab44ff4cd927aa7e5c6
--- /dev/null
+++ b/app/static/js/RessourceLists/SpacyNLPModelList.js
@@ -0,0 +1,76 @@
+class SpacyNLPModelList {
+  constructor () {
+    
+    this.elements =  {
+      spacyNLPModelList: document.querySelector('#spacy-nlp-model-list'),
+      deleteButtons: document.querySelectorAll('.delete-spacy-model-button'),
+      editButtons: document.querySelectorAll('.edit-spacy-model-button'),
+      
+    }
+  }
+
+  init () {
+    let userId = this.elements.spacyNLPModelList.dataset.userId;
+    
+    for (let deleteButton of this.elements.deleteButtons) {
+      deleteButton.addEventListener('click', () => {this.deleteModel(deleteButton, userId);});
+    }
+
+    for (let editButton of this.elements.editButtons) {
+      editButton.addEventListener('click', () => {this.editModel(editButton);});
+    }
+  }
+
+  deleteModel(deleteButton, userId) {
+    return new Promise((resolve, reject) => {
+      let modelId = deleteButton.dataset.modelId;
+      let model = app.data.users[userId].spacy_nlp_pipeline_models[modelId];
+      let modalElement = Utils.elementFromString(
+        `
+          <div class="modal">
+            <div class="modal-content">
+              <h4>Confirm job deletion</h4>
+              <p>Do you really want to delete <b>${model.title}</b>? All files will be permanently deleted!</p>
+            </div>
+            <div class="modal-footer">
+              <a class="action-button btn modal-close waves-effect waves-light" data-action="cancel">Cancel</a>
+              <a class="action-button btn modal-close red waves-effect waves-light" data-action="confirm">Delete</a>
+            </div>
+          </div>
+        `
+      );
+      document.querySelector('#modals').appendChild(modalElement);
+      let modal = M.Modal.init(
+        modalElement,
+        {
+          dismissible: false,
+          onCloseEnd: () => {
+            modal.destroy();
+            modalElement.remove();
+          }
+        }
+      );
+      let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]');
+      confirmElement.addEventListener('click', (event) => {
+        let modelTitle = model.title;
+        fetch(`/contributions/edit-spacy-model/${modelId}`, {method: 'DELETE'})
+          .then(
+            (response) => {
+              app.flash(`Model "${modelTitle}" marked for deletion`, 'corpus');
+              resolve(response);
+            },
+            (response) => {
+              if (response.status === 403) {app.flash('Forbidden', 'error');}
+              if (response.status === 404) {app.flash('Not Found', 'error');}
+              reject(response);
+            }
+          );
+      });
+      modal.open();
+    });
+  }
+
+  editModel(editButton) {
+    window.location.href = `/contributions/edit-spacy-model/${editButton.dataset.modelId}`;
+  }
+}
diff --git a/app/static/js/RessourceLists/TesseractOCRModelList.js b/app/static/js/RessourceLists/TesseractOCRModelList.js
index 9080447e361a688ec7dd06595744c2a5f6a33d06..782f5d7eed368bb2aa1c9becda74df9c304f8796 100644
--- a/app/static/js/RessourceLists/TesseractOCRModelList.js
+++ b/app/static/js/RessourceLists/TesseractOCRModelList.js
@@ -25,7 +25,6 @@ class TesseractOCRModelList {
     return new Promise((resolve, reject) => {
       let modelId = deleteButton.dataset.modelId;
       let model = app.data.users[userId].tesseract_ocr_pipeline_models[modelId];
-      
       let modalElement = Utils.elementFromString(
         `
           <div class="modal">
@@ -54,7 +53,7 @@ class TesseractOCRModelList {
       let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]');
       confirmElement.addEventListener('click', (event) => {
         let modelTitle = model.title;
-        fetch(`/contributions/${modelId}`, {method: 'DELETE'})
+        fetch(`/contributions/edit-tesseract-model/${modelId}`, {method: 'DELETE'})
           .then(
             (response) => {
               app.flash(`Model "${modelTitle}" marked for deletion`, 'corpus');
@@ -72,6 +71,6 @@ class TesseractOCRModelList {
   }
 
   editModel(editButton) {
-    window.location.href = `/contributions/${editButton.dataset.modelId}`;
+    window.location.href = `/contributions/edit-tesseract-model/${editButton.dataset.modelId}`;
   }
 }
diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2
index 3b93ef668553148b6a0ccc24f67d1b11af3d2fac..798d2848ea1f83f74b9de9086364aa22eaa579f3 100644
--- a/app/templates/_scripts.html.j2
+++ b/app/templates/_scripts.html.j2
@@ -25,6 +25,7 @@
   'js/RessourceLists/JobInputList.js',
   'js/RessourceLists/JobResultList.js',
   'js/RessourceLists/QueryResultList.js',
+  'js/RessourceLists/SpacyNLPModelList.js',
   'js/RessourceLists/TesseractOCRModelList.js',
   'js/RessourceLists/UserList.js'
 %}
diff --git a/app/templates/contributions/_breadcrumbs.html.j2 b/app/templates/contributions/_breadcrumbs.html.j2
index 9d49da6868d6292d7caac41dee7d80262e3f25c3..4ccfad3ba57b1874a23879b07fb17b8fc6115042 100644
--- a/app/templates/contributions/_breadcrumbs.html.j2
+++ b/app/templates/contributions/_breadcrumbs.html.j2
@@ -14,5 +14,18 @@
 <li class="tab"><a href="{{ url_for('.contributions', tesseract_ocr_pipeline_model_id=nn) }}" target="_self">Contributions Overview</a></li>
 <li class="tab disabled"><i class="material-icons">navigate_next</i></li>
 <li class="tab"><a class="active" href="{{ url_for('.add_tesseract_ocr_pipeline_model') }}" target="_self">{{ title }}</a></li>
+
+{% elif request.path == url_for('.spacy_nlp_pipeline_model', spacy_nlp_pipeline_model_id=spacy_nlp_pipeline_model.id) %}
+<li class="tab"><a href="{{ url_for('.contributions') }}" target="_self">Contributions Overview</a></li>
+<li class="tab disabled"><i class="material-icons">navigate_next</i></li>
+<li class="tab">
+  <a class="active" href="{{ url_for('.spacy_nlp_pipeline_model', spacy_nlp_pipeline_model_id=spacy_nlp_pipeline_model.hashid) }}" target="_self">
+    Edit {{ spacy_nlp_pipeline_model.title }}
+  </a>
+</li>
+{% elif request.path == url_for('.add_spacy_nlp_pipeline_model, spacy_nlp_pipeline_model=nn') %}
+<li class="tab"><a href="{{ url_for('.contributions', spacy_nlp_pipeline_model_id=nn) }}" target="_self">Contributions Overview</a></li>
+<li class="tab disabled"><i class="material-icons">navigate_next</i></li>
+<li class="tab"><a class="active" href="{{ url_for('.add_spacy_nlp_pipeline_model') }}" target="_self">{{ title }}</a></li>
 {% endif %}
 {% endset %}
diff --git a/app/templates/contributions/contribute.html.j2 b/app/templates/contributions/contribute.html.j2
deleted file mode 100644
index 6789e1f85a7c8215c2b4d53ee21f0f5b33d059c2..0000000000000000000000000000000000000000
--- a/app/templates/contributions/contribute.html.j2
+++ /dev/null
@@ -1,32 +0,0 @@
-{% extends "base.html.j2" %}
-{% import "materialize/wtf.html.j2" as wtf %}
-
-
-{% block page_content %}
-<div class="container">
-  <div class="row">
-    <div class="col s12 m8 offset-m2">
-      <h1 id="title">{{ title }}</h1>
-      <p>
-        In order to add a new model, please fill in the form below.
-      </p>
-
-      <form method="POST">
-        <div class="card-panel">
-          {{ form.hidden_tag() }}
-          {{ wtf.render_field(form.title) }}
-          {{ wtf.render_field(form.description) }}
-          {{ wtf.render_field(form.publisher) }}
-          {{ wtf.render_field(form.publisher_url) }}
-          {{ wtf.render_field(form.publishing_url) }}
-          {{ wtf.render_field(form.publishing_year) }}
-          {{ wtf.render_field(form.shared) }}
-          {{ wtf.render_field(form.version) }}
-          {{ wtf.render_field(form.compatible_service_versions) }}
-          {{ wtf.render_field(form.submit, class_='width-100', material_icon='send') }}
-
-        </div>
-      </form>
-  </div>
-</div>
-{% endblock page_content %}
\ No newline at end of file
diff --git a/app/templates/contributions/contribute_spacy_nlp_models.html.j2 b/app/templates/contributions/contribute_spacy_nlp_models.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..97475c400cdc97fc56390dacd6cba4d97202fcc0
--- /dev/null
+++ b/app/templates/contributions/contribute_spacy_nlp_models.html.j2
@@ -0,0 +1,124 @@
+{% extends "base.html.j2" %}
+{% import "materialize/wtf.html.j2" as wtf %}
+{# {% from "contributions/_breadcrumbs.html.j2" import breadcrumbs with context %} #}
+
+{% block main_attribs %} class="service-scheme" data-service="tesseract-ocr-pipeline"{% endblock main_attribs %}
+
+{% block page_content %}
+<div class="container">
+  <div class="row">
+    <div class="col s12">
+      <h1 id="title">{{ title }}</h1>
+    </div>
+
+    <div class="col s12 m3 push-m9">
+      <div class="center-align">
+        <p class="hide-on-small-only">&nbsp;</p>
+        <p class="hide-on-small-only">&nbsp;</p>
+        <a class="btn-floating btn-large btn-scale-x2 waves-effect waves-light">
+          <i class="nopaque-icons service-color darken service-icon" data-service="spacy-nlp-pipeline"></i>
+        </a>
+      </div>
+    </div>
+
+    <div class="col s12 m9 pull-m3">
+      <div class="card service-color-border border-darken" data-service="spacy-nlp-pipeline" style="border-top: 10px solid;">
+        <div class="card-content">
+          <div class="row">
+            <div class="col s12">
+              <div class="card-panel z-depth-0">
+                <span class="card-title"><i class="left material-icons">layers</i>spaCy NLP Models</span>
+                <p>You can add more Tesseract OCR models using the form below. They will automatically appear in the list of usable models.</p>
+                <p><a href="">Edit already uploaded models</a></p>
+                <p><a class="modal-trigger" href="#models-modal">Information about the already existing models.</a></p>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="col s12">
+      <h2>Add a model</h2>
+      <div class="card">
+        <form class="create-contribution-form" enctype="multipart/form-data" method="POST">
+          <div class="card-content">
+            {{ form.hidden_tag() }}
+            <div class="row">
+              <div class="col s12 l5">
+                {{ wtf.render_field(form.spacy_model_file, accept='.tar.gz', placeholder='Choose a .tar.gz file') }}
+              </div>
+              <div class="col s12 l7">
+                {{ wtf.render_field(form.title, material_icon='title') }}
+              </div>
+              <div class="col s12">
+                {{ wtf.render_field(form.description, material_icon='description') }}
+              </div>
+              <div class="col s12 l6">
+                {{ wtf.render_field(form.publisher,  material_icon='account_balance') }}
+              </div>
+              <div class="col s12 l6">
+                {{ wtf.render_field(form.publishing_year, material_icon='calendar_month') }}
+              </div>
+              <div class="col s12">
+                {{ wtf.render_field(form.publisher_url, material_icon='link') }}
+              </div>
+              <div class="col s12">
+                {{ wtf.render_field(form.publishing_url, material_icon='link') }}
+              </div>
+              <div class="col s12 l10">
+                {{ wtf.render_field(form.version, material_icon='apps') }}
+              </div>
+              <div class="col s12 l6">
+                {{ wtf.render_field(form.compatible_service_versions) }}
+              </div>
+              <div class="col s12 l6 right-align" style="padding-right:20px;">
+                <p></p>
+                <br>
+                {{ wtf.render_field(form.shared) }}
+              </div>
+            </div>
+          </div>
+          <div class="card-action right-align">
+            {{ wtf.render_field(form.submit, material_icon='send') }}
+          </div>
+        </form>
+      </div>
+    </div>
+  </div>
+</div>
+{% endblock page_content %}
+
+{% block modals %}
+{{ super() }}
+<div id="models-modal" class="modal">
+  <div class="modal-content">
+    <h4>spaCy NLP Pipeline models</h4>
+    <table>
+      <thead>
+        <tr>
+          <th>Title</th>
+          <th>Description</th>
+          <th>Biblio</th>
+        </tr>
+      </thead>
+      <tbody>
+        {% for m in spacy_nlp_pipeline_models %}
+        <tr id="spacy-nlp-pipeline-model-{{ m.hashid }}">
+          <td>{{ m.title }}</td>
+          {% if m.description == '' %}
+          <td>Description is not available.</td>
+          {% else %}
+          <td>{{ m.description }}</td>
+          {% endif %}
+          <td><a href="{{ m.publisher_url }}">{{ m.publisher }}</a> ({{ m.publishing_year }}), {{ m.title }} {{ m.version}}, <a href="{{ m.publishing_url }}">{{ m.publishing_url }}</a></td>
+        </tr>
+        {% endfor %}
+      </tbody>
+    </table>
+  </div>
+  <div class="modal-footer">
+    <a href="#!" class="modal-close waves-effect waves-light btn">Close</a>
+  </div>
+</div>
+{% endblock modals %}
diff --git a/app/templates/contributions/contribution_overview.html.j2 b/app/templates/contributions/contribution_overview.html.j2
index 6a1ebb1ed3e5d18046480aecb276a2a1dd8873cb..c6facf9d7c813915444b87497a0c591cec413815 100644
--- a/app/templates/contributions/contribution_overview.html.j2
+++ b/app/templates/contributions/contribution_overview.html.j2
@@ -28,8 +28,8 @@
                       </tr>
                     </thead>
                     <tbody>
-                      {% if tesseractOCRUserModels|length > 0 %}
-                        {% for m in tesseractOCRUserModels %}
+                      {% if tesseract_ocr_user_models|length > 0 %}
+                        {% for m in tesseract_ocr_user_models %}
                           <tr id="tesseract-ocr-pipeline-model-{{ m.hashid }}">
                             <td>{{ m.title }}</td>
                             {% if m.description == '' %}
@@ -61,6 +61,58 @@
         </div>
       </div>
       
+      {# spaCy NLP Models #}
+      <div>
+        <h3>My spaCy NLP Pipeline Models</h3>
+        <p>Here you can see and edit the models that you have created. You can also create new models.</p>
+
+        <div class="row">
+          <div class="col s12">
+            <div class="card">
+              <div class="card-content">
+                <div id="spacy-nlp-model-list" data-user-id="{{ userId }}" data-user-models="{{ spacy_nlp_user_models }}">
+                  <table>
+                    <thead>
+                      <tr>
+                        <th>Title</th>
+                        <th>Description</th>
+                        <th>Biblio</th>
+                        <th></th>
+                      </tr>
+                    </thead>
+                    <tbody>
+                      {% if spacy_nlp_user_models|length > 0 %}
+                        {% for m in spacy_nlp_user_models %}
+                          <tr id="spacy_nlp-pipeline-model-{{ m.hashid }}">
+                            <td>{{ m.title }}</td>
+                            {% if m.description == '' %}
+                            <td>Description is not available.</td>
+                            {% else %}
+                            <td>{{ m.description }}</td>
+                            {% endif %}
+                            <td><a href="{{ m.publisher_url }}">{{ m.publisher }}</a> ({{ m.publishing_year }}), {{ m.title }} {{ m.version}}, <a href="{{ m.publishing_url }}">{{ m.publishing_url }}</a></td>
+                            <td class="right-align">
+                              <a class="delete-spacy-model-button btn-floating red waves-effect waves-light" data-model-id="{{ m.hashid }}"><i class="material-icons">delete</i></a>
+                              <a class="edit-spacy-model-button btn-floating service-color darken waves-effect waves-light" data-model-id="{{ m.hashid }}"><i class="material-icons">edit</i></a>
+                            </td>
+                          </tr>
+                        {% endfor %}
+                      {% else %}
+                        <tr>
+                          <td colspan="4">No models available.</td>
+                        </tr>
+                      {% endif %}
+                    </tbody>
+                  </table>
+                </div>
+              </div>
+              <div class="card-action right-align">
+                <a href="{{ url_for('contributions.add_spacy_nlp_pipeline_model') }}" class="btn waves-effect waves-light"><i class="material-icons left">add</i>Add model file</a>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
     </div>
   </div>
 </div>
@@ -71,5 +123,7 @@
 <script>
 const tesseractOCRModelList = new TesseractOCRModelList();
 tesseractOCRModelList.init();
+const spacyNLPModelList = new SpacyNLPModelList();
+spacyNLPModelList.init();
 </script>
 {% endblock scripts %}
diff --git a/app/templates/contributions/spacy_nlp_pipeline_model.html.j2 b/app/templates/contributions/spacy_nlp_pipeline_model.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..04d7506fbfaaa3b21c36271004ed3289b67498a9
--- /dev/null
+++ b/app/templates/contributions/spacy_nlp_pipeline_model.html.j2
@@ -0,0 +1,56 @@
+{% extends "base.html.j2" %}
+{% import "materialize/wtf.html.j2" as wtf %}
+{# {% from "contributions/_breadcrumbs.html.j2" import breadcrumbs with context %} #}
+
+{% block main_attribs %} class="service-scheme" data-service="spacy-nlp-pipeline"{% endblock main_attribs %}
+
+{% block page_content %}
+<div class="container">
+  <div class="row">
+    <div class="col s12">
+      <h1 id="title">{{ title }}</h1>
+    </div>
+
+    <div class="col s12">
+      <div class="card">
+        <form class="create-contribution-form" enctype="multipart/form-data" method="POST">
+          <div class="card-content">
+            {{ form.hidden_tag() }}
+            <div class="row">
+              <div class="col s12 l7">
+                {{ wtf.render_field(form.title, material_icon='title') }}
+              </div>
+              <div class="col s12">
+                {{ wtf.render_field(form.description, material_icon='description') }}
+              </div>
+              <div class="col s12 l6">
+                {{ wtf.render_field(form.publisher, material_icon='account_balance') }}
+              </div>
+              <div class="col s12 l6">
+                {{ wtf.render_field(form.publishing_year, material_icon='calendar_month') }}
+              </div>
+              <div class="col s12">
+                {{ wtf.render_field(form.publisher_url, material_icon='link') }}
+              </div>
+              <div class="col s12">
+                {{ wtf.render_field(form.publishing_url, material_icon='link') }}
+              </div>
+              <div class="col s12 l10">
+                {{ wtf.render_field(form.version, material_icon='apps') }}
+              </div>
+              <div class="col s12 l6 right-align" style="padding-right:20px;">
+                <p></p>
+                <br>
+                {{ wtf.render_field(form.shared) }}
+              </div>
+            </div>
+          </div>
+          <div class="card-action right-align">
+            {{ wtf.render_field(form.submit, material_icon='send') }}
+          </div>
+        </form>
+      </div>
+    </div>
+  </div>
+</div>
+{% endblock page_content %}
diff --git a/migrations/versions/721829b5dd25_.py b/migrations/versions/721829b5dd25_.py
new file mode 100644
index 0000000000000000000000000000000000000000..124ca07d15f2ccd126a4993bd05fc7e9969fb1ad
--- /dev/null
+++ b/migrations/versions/721829b5dd25_.py
@@ -0,0 +1,28 @@
+"""empty message
+
+Revision ID: 721829b5dd25
+Revises: 31dd42e5ea6f
+Create Date: 2022-11-04 13:58:13.008301
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '721829b5dd25'
+down_revision = '31dd42e5ea6f'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.add_column('spacy_nlp_pipeline_models', sa.Column('pipeline_name', sa.String(length=64), nullable=True))
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column('spacy_nlp_pipeline_models', 'pipeline_name')
+    # ### end Alembic commands ###