diff --git a/app/admin/forms.py b/app/admin/forms.py
index 7b468161851a6936383158a594d10cd6cd222c2b..b063b938a45ce939764d96ecb4733775f22621ea 100644
--- a/app/admin/forms.py
+++ b/app/admin/forms.py
@@ -1,9 +1,9 @@
-from app.models import Role
-from flask_wtf import FlaskForm
 from wtforms import BooleanField, SelectField, SubmitField
+from app.forms import NopaqueForm
+from app.models import Role
 
 
-class AdminEditUserForm(FlaskForm):
+class AdminEditUserForm(NopaqueForm):
     confirmed = BooleanField('Confirmed')
     role = SelectField('Role')
     submit = SubmitField('Submit')
diff --git a/app/auth/forms.py b/app/auth/forms.py
index 6917b78baf12fe2028a5c2bd27bdcda87d8ac605..5655b3af9fbcb697757ad39e39d4c5c5c0c08441 100644
--- a/app/auth/forms.py
+++ b/app/auth/forms.py
@@ -1,4 +1,3 @@
-from flask_wtf import FlaskForm
 from wtforms import (
     BooleanField,
     PasswordField,
@@ -7,11 +6,12 @@ from wtforms import (
     ValidationError
 )
 from wtforms.validators import InputRequired, Email, EqualTo, Length, Regexp
+from app.forms import NopaqueForm
 from app.models import User
 from . import USERNAME_REGEX
 
 
-class RegistrationForm(FlaskForm):
+class RegistrationForm(NopaqueForm):
     email = StringField(
         'Email',
         validators=[InputRequired(), Email(), Length(max=254)]
@@ -55,19 +55,19 @@ class RegistrationForm(FlaskForm):
             raise ValidationError('Username already in use')
 
 
-class LoginForm(FlaskForm):
+class LoginForm(NopaqueForm):
     user = StringField('Email or username', validators=[InputRequired()])
     password = PasswordField('Password', validators=[InputRequired()])
     remember_me = BooleanField('Keep me logged in')
     submit = SubmitField()
 
 
-class ResetPasswordRequestForm(FlaskForm):
+class ResetPasswordRequestForm(NopaqueForm):
     email = StringField('Email', validators=[InputRequired(), Email()])
     submit = SubmitField()
 
 
-class ResetPasswordForm(FlaskForm):
+class ResetPasswordForm(NopaqueForm):
     password = PasswordField(
         'New password',
         validators=[
diff --git a/app/auth/routes.py b/app/auth/routes.py
index 1f30d58ae67d1c6d5ae6ad87b3a08521e07ae4b0..dc6140f21710014afb1a41eb169048b2fe10bec1 100644
--- a/app/auth/routes.py
+++ b/app/auth/routes.py
@@ -34,7 +34,7 @@ def before_request():
 def register():
     if current_user.is_authenticated:
         return redirect(url_for('main.dashboard'))
-    form = RegistrationForm(prefix='registration-form')
+    form = RegistrationForm()
     if form.validate_on_submit():
         try:
             user = User.create(
@@ -70,7 +70,7 @@ def register():
 def login():
     if current_user.is_authenticated:
         return redirect(url_for('main.dashboard'))
-    form = LoginForm(prefix='login-form')
+    form = LoginForm()
     if form.validate_on_submit():
         user = User.query.filter((User.email == form.user.data.lower()) | (User.username == form.user.data)).first()
         if user and user.verify_password(form.password.data):
@@ -144,7 +144,7 @@ def confirm(token):
 def reset_password_request():
     if current_user.is_authenticated:
         return redirect(url_for('main.dashboard'))
-    form = ResetPasswordRequestForm(prefix='reset-password-request-form')
+    form = ResetPasswordRequestForm()
     if form.validate_on_submit():
         user = User.query.filter_by(email=form.email.data.lower()).first()
         if user is not None:
@@ -174,7 +174,7 @@ def reset_password_request():
 def reset_password(token):
     if current_user.is_authenticated:
         return redirect(url_for('main.dashboard'))
-    form = ResetPasswordForm(prefix='reset-password-form')
+    form = ResetPasswordForm()
     if form.validate_on_submit():
         if User.reset_password(token, form.password.data):
             db.session.commit()
diff --git a/app/contributions/forms.py b/app/contributions/forms.py
index acec307f00b4e409ff962cc7e0021973a442f2fe..46777ff08b428671350edd65ab88793cac7e38f7 100644
--- a/app/contributions/forms.py
+++ b/app/contributions/forms.py
@@ -1,4 +1,3 @@
-from flask_wtf import FlaskForm
 from wtforms import (
     StringField,
     SubmitField,
@@ -6,9 +5,10 @@ from wtforms import (
     IntegerField
 )
 from wtforms.validators import InputRequired, Length
+from app.forms import NopaqueForm
 
 
-class ContributionBaseForm(FlaskForm):
+class ContributionBaseForm(NopaqueForm):
     title = StringField(
         'Title',
         validators=[InputRequired(), Length(max=64)]
diff --git a/app/contributions/spacy_nlp_pipeline_models/routes.py b/app/contributions/spacy_nlp_pipeline_models/routes.py
index 91dee8c71c11b68214a6b8c293234b6bfa485a50..eab8ac49f5257f212f2d85d0c32b2b849d4d17e9 100644
--- a/app/contributions/spacy_nlp_pipeline_models/routes.py
+++ b/app/contributions/spacy_nlp_pipeline_models/routes.py
@@ -27,8 +27,7 @@ def spacy_nlp_pipeline_models():
 @register_breadcrumb(bp, '.spacy_nlp_pipeline_models.create', 'Create')
 @login_required
 def create_spacy_nlp_pipeline_model():
-    form_prefix = 'create-spacy-nlp-pipeline-model-form'
-    form = CreateSpaCyNLPPipelineModelForm(prefix=form_prefix)
+    form = CreateSpaCyNLPPipelineModelForm()
     if form.is_submitted():
         if not form.validate():
             return {'errors': form.errors}, 400
@@ -64,11 +63,7 @@ def create_spacy_nlp_pipeline_model():
 @login_required
 def spacy_nlp_pipeline_model(spacy_nlp_pipeline_model_id):
     snpm = SpaCyNLPPipelineModel.query.get_or_404(spacy_nlp_pipeline_model_id)
-    form_prefix = 'edit-spacy-nlp-pipeline-model-form'
-    form = EditSpaCyNLPPipelineModelForm(
-        data=snpm.to_json_serializeable(),
-        prefix=form_prefix
-    )
+    form = EditSpaCyNLPPipelineModelForm(data=snpm.to_json_serializeable())
     if form.validate_on_submit():
         form.populate_obj(snpm)
         if db.session.is_modified(snpm):
diff --git a/app/contributions/tesseract_ocr_pipeline_models/routes.py b/app/contributions/tesseract_ocr_pipeline_models/routes.py
index 31552a598f5c3b62c10248556cd3d6e06b237298..51adf402e6e310d2e64852c2d50a97a103edb48f 100644
--- a/app/contributions/tesseract_ocr_pipeline_models/routes.py
+++ b/app/contributions/tesseract_ocr_pipeline_models/routes.py
@@ -27,8 +27,7 @@ def tesseract_ocr_pipeline_models():
 @register_breadcrumb(bp, '.tesseract_ocr_pipeline_models.create', 'Create')
 @login_required
 def create_tesseract_ocr_pipeline_model():
-    form_prefix = 'create-tesseract-ocr-pipeline-model-form'
-    form = CreateTesseractOCRPipelineModelForm(prefix=form_prefix)
+    form = CreateTesseractOCRPipelineModelForm()
     if form.is_submitted():
         if not form.validate():
             return {'errors': form.errors}, 400
@@ -63,11 +62,7 @@ def create_tesseract_ocr_pipeline_model():
 @login_required
 def tesseract_ocr_pipeline_model(tesseract_ocr_pipeline_model_id):
     topm = TesseractOCRPipelineModel.query.get_or_404(tesseract_ocr_pipeline_model_id)
-    form_prefix = 'edit-tesseract-ocr-pipeline-model-form'
-    form = EditTesseractOCRPipelineModelForm(
-        data=topm.to_json_serializeable(),
-        prefix=form_prefix
-    )
+    form = EditTesseractOCRPipelineModelForm(data=topm.to_json_serializeable())
     if form.validate_on_submit():
         form.populate_obj(topm)
         if db.session.is_modified(topm):
diff --git a/app/corpora/files/forms.py b/app/corpora/files/forms.py
index f5b0f0c86492471840b76826897c131584723de1..c819fba2224cd701e86ff37112ac490934584985 100644
--- a/app/corpora/files/forms.py
+++ b/app/corpora/files/forms.py
@@ -1,4 +1,3 @@
-from flask_wtf import FlaskForm
 from flask_wtf.file import FileField, FileRequired
 from wtforms import (
     StringField,
@@ -7,9 +6,10 @@ from wtforms import (
     IntegerField
 )
 from wtforms.validators import InputRequired, Length
+from app.forms import NopaqueForm
 
 
-class CorpusFileBaseForm(FlaskForm):
+class CorpusFileBaseForm(NopaqueForm):
     author = StringField(
         'Author',
         validators=[InputRequired(), Length(max=255)]
@@ -37,18 +37,10 @@ class CorpusFileBaseForm(FlaskForm):
 class CreateCorpusFileForm(CorpusFileBaseForm):
     vrt = FileField('File', validators=[FileRequired()])
 
-    def __init__(self, *args, **kwargs):
-        if 'prefix' not in kwargs:
-            kwargs['prefix'] = 'create-corpus-file-form'
-        super().__init__(*args, **kwargs)
-
     def validate_vrt(self, field):
         if not field.data.filename.lower().endswith('.vrt'):
             raise ValidationError('VRT files only!')
 
 
 class UpdateCorpusFileForm(CorpusFileBaseForm):
-    def __init__(self, *args, **kwargs):
-        if 'prefix' not in kwargs:
-            kwargs['prefix'] = 'update-corpus-file-form'
-        super().__init__(*args, **kwargs)
+    pass
diff --git a/app/corpora/forms.py b/app/corpora/forms.py
index 36ee34c8ccc43c754f46b8d5d98bba53e1fd139e..e923ca1b9a38200f24c651c7c1e571af58d0bd0c 100644
--- a/app/corpora/forms.py
+++ b/app/corpora/forms.py
@@ -1,9 +1,9 @@
-from flask_wtf import FlaskForm
 from wtforms import StringField, SubmitField, TextAreaField
 from wtforms.validators import InputRequired, Length
+from app.forms import NopaqueForm
 
 
-class CorpusBaseForm(FlaskForm):
+class CorpusBaseForm(NopaqueForm):
     description = TextAreaField(
         'Description',
         validators=[InputRequired(), Length(max=255)]
@@ -13,18 +13,12 @@ class CorpusBaseForm(FlaskForm):
 
 
 class CreateCorpusForm(CorpusBaseForm):
-    def __init__(self, *args, **kwargs):
-        if 'prefix' not in kwargs:
-            kwargs['prefix'] = 'create-corpus-form'
-        super().__init__(*args, **kwargs)
+    pass
 
 
 class UpdateCorpusForm(CorpusBaseForm):
-    def __init__(self, *args, **kwargs):
-        if 'prefix' not in kwargs:
-            kwargs['prefix'] = 'update-corpus-form'
-        super().__init__(*args, **kwargs)
+    pass
 
 
-class ImportCorpusForm(FlaskForm):
+class ImportCorpusForm(NopaqueForm):
     pass
diff --git a/app/forms.py b/app/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..55e80d01ec1b2d4a960a2c6f631a3246bba7671d
--- /dev/null
+++ b/app/forms.py
@@ -0,0 +1,9 @@
+from flask_wtf import FlaskForm
+import re
+
+
+class NopaqueForm(FlaskForm):
+    def __init__(self, *args, **kwargs):
+        if 'prefix' not in kwargs:
+            kwargs['prefix'] = re.sub(r'(?<!^)(?=[A-Z])', '-', self.__class__.__name__).lower()
+        super().__init__(*args, **kwargs)
diff --git a/app/main/routes.py b/app/main/routes.py
index b14b3ae9053599d3e64568822af67894a49049e5..f5fac68dbc63db5d21825ec240709d62a16d241c 100644
--- a/app/main/routes.py
+++ b/app/main/routes.py
@@ -9,7 +9,7 @@ from . import bp
 @bp.route('', methods=['GET', 'POST'])
 @register_breadcrumb(bp, '.', '<i class="material-icons">home</i>')
 def index():
-    form = LoginForm(prefix='login-form')
+    form = LoginForm()
     if form.validate_on_submit():
         user = User.query.filter((User.email == form.user.data.lower()) | (User.username == form.user.data)).first()
         if user and user.verify_password(form.password.data):
diff --git a/app/services/forms.py b/app/services/forms.py
index 1ad544dcd72be41e26a917d97ad2a64dfb2ac2e0..d53132f8c714c29113d08dc5651d7ea75af08f79 100644
--- a/app/services/forms.py
+++ b/app/services/forms.py
@@ -1,16 +1,21 @@
 from flask_login import current_user
-from flask_wtf import FlaskForm
 from flask_wtf.file import FileField, FileRequired
-from wtforms import (BooleanField, DecimalRangeField, MultipleFileField,
-                     SelectField, StringField, SubmitField, ValidationError)
+from wtforms import (
+    BooleanField,
+    DecimalRangeField,
+    MultipleFileField,
+    SelectField,
+    StringField,
+    SubmitField,
+    ValidationError
+)
 from wtforms.validators import InputRequired, Length
-
+from app.forms import NopaqueForm
 from app.models import SpaCyNLPPipelineModel, TesseractOCRPipelineModel
-
 from . import SERVICES
 
 
-class CreateJobBaseForm(FlaskForm):
+class CreateJobBaseForm(NopaqueForm):
     description = StringField(
         'Description',
         validators=[InputRequired(), Length(max=255)]
diff --git a/app/services/routes.py b/app/services/routes.py
index 324aab6398bd0526e8d50bb0a5b32686abc40cf3..3a3cbd7b409a1928e6f27e908cff61b1eafc420c 100644
--- a/app/services/routes.py
+++ b/app/services/routes.py
@@ -35,7 +35,7 @@ def file_setup_pipeline():
     version = request.args.get('version', service_manifest['latest_version'])
     if version not in service_manifest['versions']:
         abort(404)
-    form = CreateFileSetupPipelineJobForm(prefix='create-job-form', version=version)
+    form = CreateFileSetupPipelineJobForm(version=version)
     if form.is_submitted():
         if not form.validate():
             response = {'errors': form.errors}
@@ -77,7 +77,7 @@ def tesseract_ocr_pipeline():
     version = request.args.get('version', service_manifest['latest_version'])
     if version not in service_manifest['versions']:
         abort(404)
-    form = CreateTesseractOCRPipelineJobForm(prefix='create-job-form', version=version)
+    form = CreateTesseractOCRPipelineJobForm(version=version)
     if form.is_submitted():
         if not form.validate():
             response = {'errors': form.errors}
@@ -140,7 +140,6 @@ def transkribus_htr_pipeline():
     print(transkribus_htr_pipeline_models[len(transkribus_htr_pipeline_models)-1])
     form = CreateTranskribusHTRPipelineJobForm(
         transkribus_htr_pipeline_models=transkribus_htr_pipeline_models,
-        prefix='create-job-form',
         version=version
     )
     if form.is_submitted():
@@ -187,7 +186,7 @@ def spacy_nlp_pipeline():
     version = request.args.get('version', SERVICES[service]['latest_version'])
     if version not in service_manifest['versions']:
         abort(404)
-    form = CreateSpacyNLPPipelineJobForm(prefix='create-job-form', version=version)
+    form = CreateSpacyNLPPipelineJobForm(version=version)
     spacy_nlp_pipeline_models = SpaCyNLPPipelineModel.query.all()
     if form.is_submitted():
         if not form.validate():
diff --git a/app/settings/forms.py b/app/settings/forms.py
index 39a235cc6fd74b5cde6abe1cea9c39c3e9b7b685..2cfccf5fe3dc8c187c4b9271265c84107cfa6d3a 100644
--- a/app/settings/forms.py
+++ b/app/settings/forms.py
@@ -1,5 +1,4 @@
 from flask_login import current_user
-from flask_wtf import FlaskForm
 from wtforms import (
     FileField,
     PasswordField,
@@ -16,12 +15,13 @@ from wtforms.validators import (
     Length,
     Regexp
 )
+from app.forms import NopaqueForm
 from app.models import User, UserSettingJobStatusMailNotificationLevel
 from app.auth import USERNAME_REGEX
 from app.wtf_validators import FileSizeLimit
 
 
-class EditAccountForm(FlaskForm):
+class EditAccountForm(NopaqueForm):
     email = StringField(
         'E-Mail',
         validators=[DataRequired(), Length(max=254), Email()]
@@ -46,8 +46,6 @@ class EditAccountForm(FlaskForm):
         user = kwargs.get('user', current_user._get_current_object())
         if 'data' not in kwargs:
             kwargs['data'] = user.to_json_serializeable()
-        if 'prefix' not in kwargs:
-            kwargs['prefix'] = 'edit-profile-settings-form'
         super().__init__(*args, **kwargs)
         self.user = user
 
@@ -65,7 +63,7 @@ class EditAccountForm(FlaskForm):
         return self.submit.data and self.validate()
 
 
-class EditProfileForm(FlaskForm):
+class EditProfileForm(NopaqueForm):
     avatar = FileField(
         'Image File', 
         [FileSizeLimit(max_size_in_mb=2)]
@@ -104,8 +102,6 @@ class EditProfileForm(FlaskForm):
         if 'data' not in kwargs:
             user = current_user._get_current_object()
             kwargs['data'] = user.to_json_serializeable()
-        if 'prefix' not in kwargs:
-            kwargs['prefix'] = 'edit-public-profile-information-form'
         super().__init__(*args, **kwargs)
 
     def validate_image_file(self, field):
@@ -116,7 +112,7 @@ class EditProfileForm(FlaskForm):
         return self.submit.data and self.validate()
 
 
-class ChangePasswordForm(FlaskForm):
+class ChangePasswordForm(NopaqueForm):
     password = PasswordField('Old password', validators=[DataRequired()])
     new_password = PasswordField(
         'New password',
@@ -136,8 +132,6 @@ class ChangePasswordForm(FlaskForm):
 
     def __init__(self, *args, **kwargs):
         user = kwargs.get('user', current_user._get_current_object())
-        if 'prefix' not in kwargs:
-            kwargs['prefix'] = 'change-password-form'
         super().__init__(*args, **kwargs)
         self.user = user
 
@@ -149,7 +143,7 @@ class ChangePasswordForm(FlaskForm):
         return self.submit.data and self.validate()
 
 
-class EditNotificationsForm(FlaskForm):
+class EditNotificationsForm(NopaqueForm):
     job_status_mail_notification_level = SelectField(
         'Job status mail notification level',
         choices=[
@@ -164,8 +158,6 @@ class EditNotificationsForm(FlaskForm):
         if 'data' not in kwargs:
             user = current_user._get_current_object()
             kwargs['data'] = user.to_json_serializeable()
-        if 'prefix' not in kwargs:
-            kwargs['prefix'] = 'edit-notification-settings-form'
         super().__init__(*args, **kwargs)
 
     def validate_on_submit(self):