diff --git a/aleksis/apps/paweljong/frontend/components/event/events.graphql b/aleksis/apps/paweljong/frontend/components/event/events.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..73d7458c8f5c16c14968e160674da9b9a7a14f45
--- /dev/null
+++ b/aleksis/apps/paweljong/frontend/components/event/events.graphql
@@ -0,0 +1,41 @@
+query eventById($id: ID!) {
+  eventById(id: $id) {
+    id
+    displayName
+    description
+    cost
+    minCost
+    maxCost
+    terms {
+      id
+    }
+    additionalFields {
+      id
+    }
+  }
+}
+
+query eventBySlug($slug: String!) {
+  event: eventBySlug(slug: $slug) {
+    id
+    displayName
+    description
+    cost
+    minCost
+    maxCost
+    terms {
+      id
+      title
+      term
+      confirmationText
+    }
+    additionalFields {
+      id
+      title
+      fieldType
+      required
+      helpText
+    }
+    contactInformationVisibleFields
+  }
+}
diff --git a/aleksis/apps/paweljong/frontend/components/event_additional_field/eventAdditionalFieldMixin.js b/aleksis/apps/paweljong/frontend/components/event_additional_field/eventAdditionalFieldMixin.js
new file mode 100644
index 0000000000000000000000000000000000000000..17e08f31914bd59618f3e6955585315f4ecbe671
--- /dev/null
+++ b/aleksis/apps/paweljong/frontend/components/event_additional_field/eventAdditionalFieldMixin.js
@@ -0,0 +1,38 @@
+/**
+ * Vue mixin containing code getting the respective vue component for event additional fields.
+ *
+ * Only used by event registration form, but factored out for readability.
+ */
+
+// TODO: add some more rule checking (e.g. for emails)
+
+import DateField from "aleksis.core/components/generic/forms/DateField.vue";
+import DateTimeField from "aleksis.core/components/generic/forms/DateTimeField.vue";
+import TimeField from "aleksis.core/components/generic/forms/TimeField.vue";
+import PositiveSmallIntegerField from "aleksis.core/components/generic/forms/PositiveSmallIntegerField.vue";
+
+const eventAdditionalFieldMixin = {
+  methods: {
+    fieldComponentForAdditionalField(additionalField) {
+      if (additionalField.fieldType == "CHARFIELD" || additionalField.fieldType == "EMAILFIELD" || additionalField.fieldType == "URLFIELD" || additionalField.fieldType == "GENERICIPADDRESSFIELD") {
+        return "v-text-field";
+      } else if (additionalField.fieldType == "BOOLEANFIELD" || additionalField.fieldType == "NULLBOOLEANFIELD") {
+        // TODO: implement proper null boolean input
+        return "v-checkbox"
+      } else if (additionalField.fieldType == "DATEFIELD") {
+        return DateField;
+      } else if (additionalField.fieldType == "DATETIMEFIELD") {
+        return DateTimeField;
+      } else if (additionalField.fieldType == "TIMEFIELD") {
+        return TimeField;
+      } else if (additionalField.fieldType == "INTEGERFIELD" || additionalField.fieldType == "DECIMALFIELD") {
+        // TODO: implement proper decimal input
+        return PositiveSmallIntegerField;
+      }
+      return "";
+    },
+  },
+};
+
+export default eventAdditionalFieldMixin;
+  
\ No newline at end of file
diff --git a/aleksis/apps/paweljong/frontend/components/event_registration/EventRegistrationForm.vue b/aleksis/apps/paweljong/frontend/components/event_registration/EventRegistrationForm.vue
new file mode 100644
index 0000000000000000000000000000000000000000..2251168ef7406240d97e2499ef21904beed3c7b0
--- /dev/null
+++ b/aleksis/apps/paweljong/frontend/components/event_registration/EventRegistrationForm.vue
@@ -0,0 +1,691 @@
+<script setup>
+import ControlRow from "aleksis.core/components/generic/multi_step/ControlRow.vue";
+import DateField from "aleksis.core/components/generic/forms/DateField.vue";
+import SexSelect from "aleksis.core/components/generic/forms/SexSelect.vue";
+
+import PrimaryActionButton from "aleksis.core/components/generic/buttons/PrimaryActionButton.vue";
+import SecondaryActionButton from "aleksis.core/components/generic/buttons/SecondaryActionButton.vue";
+</script>
+
+<template>
+    <main-container>
+      <div v-if="event && eventRegistrationSent">
+        <v-card>
+          <v-card-title>
+            <v-icon class="mr-2" color="success">mdi-check-circle-outline</v-icon>
+            {{ $t("paweljong.event_registration.form.submitted.thank_you") }}
+          </v-card-title>
+          <v-card-text class="text-body-1 black--text">
+            {{ $t("paweljong.event_registration.form.submitted.submitted_successfully") }}
+          </v-card-text>
+        </v-card>
+      </div>
+      <div v-else-if="event">
+        <h1 class="text-h4 mb-4">{{ event.displayName }}</h1>
+        <v-stepper v-model="step" class="mb-4">
+          <v-stepper-header>
+            <v-stepper-step :complete="step > 1" step="1">
+              {{ $t("paweljong.event_registration.form.steps.email.title") }}
+            </v-stepper-step>
+            <v-divider></v-divider>
+            <v-stepper-step :complete="step > 2" step="2">
+              {{ $t("paweljong.event_registration.form.steps.register.title") }}
+            </v-stepper-step>
+            <v-divider></v-divider>
+            <v-stepper-step :complete="step > 3" step="3">
+              {{ $t("paweljong.event_registration.form.steps.contact_details.title") }}
+            </v-stepper-step>
+            <v-divider></v-divider>
+            <v-stepper-step :complete="step > 4" step="4">
+              {{ $t("paweljong.event_registration.form.steps.guardians.title") }}
+            </v-stepper-step>
+            <v-divider></v-divider>
+            <v-stepper-step :complete="step > 5" step="5">
+              {{ $t("paweljong.event_registration.form.steps.additional.title") }}
+            </v-stepper-step>
+            <v-divider></v-divider>
+            <v-stepper-step :complete="step > 6" step="6">
+              {{ $t("paweljong.event_registration.form.steps.financial.title") }}
+            </v-stepper-step>
+            <v-divider></v-divider>
+            <v-stepper-step :complete="step > 7" step="7">
+              {{ $t("paweljong.event_registration.form.steps.consent.title") }}
+            </v-stepper-step>
+            <v-divider></v-divider>
+            <v-stepper-step :complete="step > 8" step="8">
+              {{ $t("paweljong.event_registration.form.steps.confirm.title") }}
+            </v-stepper-step>
+            <v-divider></v-divider>
+          </v-stepper-header>
+          <v-stepper-items>
+            <v-stepper-content step="1">
+              <h2 class="text-h6 mb-4">{{ $t("paweljong.event_registration.form.steps.email.title") }}</h2>
+              <div class="mb-4">
+                <v-row v-if="emailMode == 'postbuero'">
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.email.localPart"
+                      :label="$t('postbuero.mail_addresses.data_table.local_part')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.email.domain"
+                      :label="$t('postbuero.mail_addresses.data_table.domain')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                </v-row>
+                <v-row v-else-if="emailMode == 'own'">
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.user.email"
+                      :label="$t('paweljong.event_registration.form.steps.email.fields.email.label')"
+                      required
+                      :rules="rules.email"
+                      prepend-icon="mdi-email-outline"
+                    ></v-text-field>
+                  </v-col>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.user.confirmEmail"
+                      :label="$t('paweljong.event_registration.form.steps.email.fields.confirm_email.label')"
+                      required
+                      :rules="rules.confirmEmail"
+                      prepend-icon="mdi-email-outline"
+                    ></v-text-field>
+                  </v-col>
+                </v-row>
+                <template v-else>
+                  <v-card-text>
+                    {{ $t("paweljong.event_registration.form.steps.email.choose_mode.help_text") }}
+                  </v-card-text>
+                  <v-card-actions>
+                    <primary-action-button
+                      @click="emailMode = 'postbuero'"
+                      i18n-key="paweljong.event_registration.form.steps.email.choose_mode.continue_aleksis"
+                    />
+                    <primary-action-button
+                      @click="emailMode = 'own'"
+                      i18n-key="paweljong.event_registration.form.steps.email.choose_mode.continue_own"
+                    />
+                  </v-card-actions>
+                </template>
+              </div>
+              <v-divider class="mb-4" />
+              <control-row
+                :step="step"
+                @set-step="setStep"
+              />
+            </v-stepper-content>
+
+            <v-stepper-content step="2">
+              <h2 class="text-h6 mb-4">{{ $t("paweljong.event_registration.form.steps.register.title") }}</h2>
+              <div class="mb-4">
+                <v-row>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.person.firstName"
+                      :label="$t('paweljong.event_registration.form.steps.register.fields.first_name.label')"
+                      required
+                      :rules="rules.name"
+                    ></v-text-field>
+                  </v-col>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.person.lastName"
+                      :label="$t('paweljong.event_registration.form.steps.register.fields.last_name.label')"
+                      required
+                      :rules="rules.name"
+                    ></v-text-field>
+                  </v-col>
+                </v-row>
+                <v-row>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.user.username"
+                      :label="$t('paweljong.event_registration.form.steps.register.fields.username.label')"
+                      required
+                      :rules="rules.name"
+                      prepend-icon="mdi-account-outline"
+                    ></v-text-field>
+                  </v-col>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.user.password"
+                      :label="$t('paweljong.event_registration.form.steps.register.fields.password.label')"
+                      required
+                      type="password"
+                      prepend-icon="mdi-form-textbox-password"
+                    ></v-text-field>
+                  </v-col>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.user.confirmPassword"
+                      :label="$t('paweljong.event_registration.form.steps.register.fields.confirm_password.label')"
+                      required
+                      type="password"
+                      :rules="rules.confirmPassword"
+                      prepend-icon="mdi-form-textbox-password"
+                    ></v-text-field>
+                  </v-col>
+                </v-row>
+              </div>
+              <v-divider class="mb-4" />
+              <control-row
+                :step="step"
+                @set-step="setStep"
+              />
+            </v-stepper-content>
+
+            <v-stepper-content step="3">
+              <h2 class="text-h6 mb-4">{{ $t("paweljong.event_registration.form.steps.contact_details.title") }}</h2>
+              <div class="mb-4">
+                <v-row>
+                  <v-col v-if="isFieldVisible('date_of_birth')">
+                    <date-field
+                      outlined
+                      v-model="data.person.dateOfBirth"
+                      :label="$t('paweljong.event_registration.form.steps.contact_details.fields.date_of_birth.label')"
+                      required
+                      prepend-icon="mdi-cake-variant-outline"
+                    />
+                  </v-col>
+                  <v-col v-if="isFieldVisible('sex')">
+                    <sex-select
+                      outlined
+                      v-model="data.person.sex"
+                      :label="$t('paweljong.event_registration.form.steps.contact_details.fields.sex.label')"
+                      :hint="$t('paweljong.event_registration.form.steps.contact_details.fields.sex.help_text')"
+                      persistent-hint
+                      required
+                    />
+                  </v-col>
+                  <v-col v-if="isFieldVisible('mobile_number')">
+                    <v-text-field
+                      outlined
+                      v-model="data.person.mobileNumber"
+                      :label="$t('paweljong.event_registration.form.steps.contact_details.fields.mobile_number.label')"
+                      required
+                      prepend-icon="mdi-phone-outline"
+                    ></v-text-field>
+                  </v-col>
+                </v-row>
+                <v-row>
+                  <v-col v-if="isFieldVisible('street')">
+                    <v-text-field
+                      outlined
+                      v-model="data.person.address.street"
+                      :label="$t('paweljong.event_registration.form.steps.contact_details.fields.street.label')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                  <v-col v-if="isFieldVisible('housenumber')">
+                    <v-text-field
+                      outlined
+                      v-model="data.person.address.housenumber"
+                      :label="$t('paweljong.event_registration.form.steps.contact_details.fields.housenumber.label')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                </v-row>
+                <v-row>
+                  <v-col v-if="isFieldVisible('postal_code')">
+                    <v-text-field
+                      outlined
+                      v-model="data.person.address.postalCode"
+                      :label="$t('paweljong.event_registration.form.steps.contact_details.fields.postal_code.label')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                  <v-col v-if="isFieldVisible('place')">
+                    <v-text-field
+                      outlined
+                      v-model="data.person.address.place"
+                      :label="$t('paweljong.event_registration.form.steps.contact_details.fields.place.label')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                </v-row>
+                <v-row v-if="isFieldVisible('school_details')">
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.school"
+                      :label="$t('paweljong.event_registration.form.steps.contact_details.fields.school.label')"
+                      :hint="$t('paweljong.event_registration.form.steps.contact_details.fields.school.help_text')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.schoolPlace"
+                      :label="$t('paweljong.event_registration.form.steps.contact_details.fields.school_place.label')"
+                      :hint="$t('paweljong.event_registration.form.steps.contact_details.fields.school_place.help_text')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.schoolClass"
+                      :label="$t('paweljong.event_registration.form.steps.contact_details.fields.school_class.label')"
+                      :hint="$t('paweljong.event_registration.form.steps.contact_details.fields.school_class.help_text')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                </v-row>
+              </div>
+              <v-divider class="mb-4" />
+              <control-row
+                :step="step"
+                @set-step="setStep"
+              />
+            </v-stepper-content>
+
+            <v-stepper-content step="4">
+              <h2 class="text-h6 mb-4">{{ $t("paweljong.event_registration.form.steps.guardians.title") }}</h2>
+              <div class="mb-4">
+                <v-card v-for="(guardian, index) in data.person.guardians" :key="index" class="mb-4">
+                  <v-card-title>
+                    {{ $tc("paweljong.event_registration.form.steps.guardians.counter", { i: index+1 }) }}
+                  </v-card-title>
+                  <v-card-text>
+                    <v-row>
+                      <v-col>
+                        <v-text-field
+                          outlined
+                          v-model="guardian.firstName"
+                          :label="$t('paweljong.event_registration.form.steps.guardians.fields.first_name.label')"
+                          :hint="$t('paweljong.event_registration.form.steps.guardians.fields.first_name.help_text')"
+                          required
+                        ></v-text-field>
+                      </v-col>
+                      <v-col>
+                        <v-text-field
+                          outlined
+                          v-model="guardian.lastName"
+                          :label="$t('paweljong.event_registration.form.steps.guardians.fields.last_name.label')"
+                          :hint="$t('paweljong.event_registration.form.steps.guardians.fields.last_name.help_text')"
+                          required
+                        ></v-text-field>
+                      </v-col>
+                    </v-row>
+                    <v-row>
+                      <v-col>
+                        <v-text-field
+                          outlined
+                          v-model="guardian.email"
+                          :label="$t('paweljong.event_registration.form.steps.guardians.fields.email.label')"
+                          required
+                        ></v-text-field>
+                      </v-col>
+                      <v-col>
+                        <v-text-field
+                          outlined
+                          v-model="guardian.mobileNumber"
+                          :label="$t('paweljong.event_registration.form.steps.guardians.fields.mobile_number.label')"
+                          :hint="$t('paweljong.event_registration.form.steps.guardians.fields.mobile_number.help_text')"
+                          required
+                        ></v-text-field>
+                      </v-col>
+                    </v-row>
+                  </v-card-text>
+                </v-card>
+                <v-row class="mb-4">
+                  <v-col>
+                    <v-spacer />
+                    <secondary-action-button
+                      @click="addGuardian"
+                      i18n-key="paweljong.event_registration.form.steps.guardians.add"
+                    >
+                    </secondary-action-button>
+                  </v-col>
+                </v-row>
+              </div>
+              <v-divider class="mb-4" />
+              <control-row
+                :step="step"
+                @set-step="setStep"
+              />
+            </v-stepper-content>
+
+            <v-stepper-content step="5">
+              <h2 class="text-h6 mb-4">{{ $t("paweljong.event_registration.form.steps.additional.title") }}</h2>
+              <div class="mb-4">
+                <v-row>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.medicalInformation"
+                      :label="$t('paweljong.event_registration.form.steps.additional.fields.medical_information.label')"
+                      :hint="$t('paweljong.event_registration.form.steps.additional.fields.medical_information.help_text')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                </v-row>
+                <v-row v-for="additionalField in event.additionalFields" :key="`additional-field-${additionalField.id}`">
+                  <v-col>
+                    <component
+                      :is="fieldComponentForAdditionalField(additionalField)"
+                      v-model="data.additionalFields[additionalField.id]"
+                      outlined
+                      :label="additionalField.title"
+                      :hint="additionalField.helpText"
+                      persistent-hint
+                      :required="additionalField.required"
+                    />
+                  </v-col>
+                </v-row>
+              </div>
+              <v-divider class="mb-4" />
+              <control-row
+                :step="step"
+                @set-step="setStep"
+              />
+            </v-stepper-content>
+
+            <v-stepper-content step="6">
+              <h2 class="text-h6 mb-4">{{ $t("paweljong.event_registration.form.steps.financial.title") }}</h2>
+              <div class="mb-4">
+                <v-row>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.medicalInformation"
+                      :label="$t('paweljong.event_registration.form.steps.additional.fields.medical_information.label')"
+                      :hint="$t('paweljong.event_registration.form.steps.additional.fields.medical_information.help_text')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                </v-row>
+                <v-row>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.medicalInformation"
+                      :label="$t('paweljong.event_registration.form.steps.additional.fields.medical_information.label')"
+                      :hint="$t('paweljong.event_registration.form.steps.additional.fields.medical_information.help_text')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                  <v-col>
+                    <v-text-field
+                      outlined
+                      v-model="data.medicalInformation"
+                      :label="$t('paweljong.event_registration.form.steps.additional.fields.medical_information.label')"
+                      :hint="$t('paweljong.event_registration.form.steps.additional.fields.medical_information.help_text')"
+                      required
+                    ></v-text-field>
+                  </v-col>
+                </v-row>
+              </div>
+              <v-divider class="mb-4" />
+              <control-row
+                :step="step"
+                @set-step="setStep"
+              />
+            </v-stepper-content>
+
+            <v-stepper-content step="7">
+              <h2 class="text-h6 mb-4">{{ $t("paweljong.event_registration.form.steps.consent.title") }}</h2>
+              <div class="mb-4">
+                <v-card v-for="term in event.terms" :key="`term-card-${term.id}`">
+                  <v-card-title>
+                    {{ term.title }}
+                  </v-card-title>
+                  <v-card-text v-html="term.term" />
+                </v-card>
+                <v-checkbox
+                  v-for="term in event.terms"
+                  :key="`term-checkbox-${term.id}`"
+                  required
+                  :label="term.confirmationText"
+                  v-model="data.terms[term.id]"
+                />
+                <v-checkbox
+                  v-if="event.dateRetraction"
+                  required
+                  :label="$tc('paweljong.event_registration.form.steps.consent.fields.retraction_consent.label', { date: $d($parseISODate(event.dateRetraction, 'short')) })"
+                  v-model="data.retractionConsent"
+                />
+              </div>
+              <v-divider class="mb-4" />
+              <control-row
+                :step="step"
+                @set-step="setStep"
+              />
+            </v-stepper-content>
+  
+            <v-stepper-content step="8">
+              <h2 class="text-h6 mb-4">{{ $t("paweljong.event_registration.form.steps.confirm.title") }}</h2>
+              <!-- <v-list class="mb-4">
+                <v-list-item>
+                  <v-list-item-content>
+                    <v-row>
+                      <v-col class="text-body-1 font-weight-medium">
+                        <v-icon color="primary" left class="mr-4"
+                          >mdi-account-outline</v-icon
+                        >
+                        {{ $t("order.personal_data.label_name") }}
+                      </v-col>
+                      <v-col class="text-body-1">
+                        {{ data.fullName }}
+                      </v-col>
+                    </v-row>
+                  </v-list-item-content>
+                </v-list-item>
+                <v-divider />
+                <v-list-item>
+                  <v-list-item-content>
+                    <v-row>
+                      <v-col class="text-body-1 font-weight-medium">
+                        <v-icon color="primary" left class="mr-4"
+                          >mdi-email-outline</v-icon
+                        >
+                        {{ $t("order.personal_data.label_email") }}
+                      </v-col>
+                      <v-col class="text-body-1">
+                        {{ data.email }}
+                      </v-col>
+                    </v-row>
+                  </v-list-item-content>
+                </v-list-item>
+              </v-list> -->
+
+              <v-divider class="my-4" />
+  
+              <message-box type="info" class="mb-4">
+                {{ $t("paweljong.event_registration.form.steps.confirm.hint") }}
+              </message-box>
+              <ApolloMutation
+                :mutation="require('./eventRegistrationMutation.graphql')"
+                :variables="{
+                  event: event.id,
+                  eventRegistration: dataForSubmit,
+                }"
+                @done="eventRegistrationDone"
+              >
+                <template #default="{ mutate, loading, error }">
+                  <control-row
+                    :step="step"
+                    final-step
+                    @set-step="setStep"
+                    @confirm="mutate"
+                    :next-loading="loading"
+                  />
+                </template>
+              </ApolloMutation>
+            </v-stepper-content>
+          </v-stepper-items>
+        </v-stepper>
+        <v-card>
+          <v-card-text>
+            <div>HELP TEXT</div>
+          </v-card-text>
+        </v-card>
+      </div>
+      <div v-else-if="$apollo.queries.event.loading">
+        <v-skeleton-loader type="heading, text, actions" />
+      </div>
+    </main-container>
+  </template>
+  
+  <script>
+  import { eventBySlug } from "../event/events.graphql";
+
+  import eventAdditionalFieldMixin from "../event_additional_field/eventAdditionalFieldMixin";
+
+  export default {
+    name: "EventRegistrationForm",
+    apollo: {
+      event: {
+        query: eventBySlug,
+        variables() {
+          return {
+            slug: this.slug,
+          };
+        },
+      }
+    },
+    mixins: [eventAdditionalFieldMixin],
+    watch: {
+    },
+    methods: {
+      setStep(step) {
+        this.step = step;
+      },
+      eventRegistrationDone({ data }) {
+        if (data.sendEventRegistration.ok) {
+          this.eventRegistrationSent = true;
+        }
+      },
+      isFieldVisible(fieldName) {
+        return this.event?.contactInformationVisibleFields.includes(fieldName);
+      },
+      addGuardian() {
+        this.data.person.guardians.push({
+            firstName: "",
+            lastName: "",
+            email: "",
+            mobileNumber: "",
+        });
+      },
+    },
+    props: {
+      slug: {
+        type: String,
+        required: true,
+      }
+    },
+    computed: {
+      rules() {
+        return {
+          name: [
+            (v) => !!v || this.$t("order.rules.name.required"),
+            (v) => v.length <= 255 || this.$t("order.rules.name.max"),
+          ],
+          email: [
+            (v) => /.+@.+\..+/.test(v) || this.$t("paweljong.event_registration.form.rules.email.valid"),
+          ],
+          confirmEmail: [
+            (v) => this.data.user.email == v || this.$t("paweljong.event_registration.form.rules.confirm_email.no_match"),
+          ],
+          confirmPassword: [
+            (v) => this.data.user.password == v || this.$t("paweljong.event_registration.form.rules.confirm_password.no_match"),
+          ],
+          street: [
+            (v) => !!v || this.$t("order.rules.street.required"),
+            (v) => v.length <= 255 || this.$t("order.rules.street.max"),
+          ],
+          housenumber: [
+            (v) => !!v || this.$t("order.rules.housenumber.required"),
+            (v) => v.length <= 255 || this.$t("order.rules.housenumber.max"),
+          ],
+          postalCode: [
+            (v) => !!v || this.$t("order.rules.postal_code.required"),
+            (v) => /^\d{5}$/.test(v) || this.$t("order.rules.postal_code.valid"),
+          ],
+          place: [
+            (v) => !!v || this.$t("order.rules.place.required"),
+            (v) => v.length <= 255 || this.$t("order.rules.place.max"),
+          ],
+        };
+      },
+      dataForSubmit() {
+        return {
+          ...this.data,
+        };
+      },
+    },
+    data() {
+      return {
+        eventRegistrationSent: false,
+        step: 1,
+        emailMode: null,
+        data: {
+          email: {
+            localPart: "",
+            domain: "",
+          },
+          person: {
+            firstName: "",
+            lastName: "",
+            dateOfBirth: "",
+            sex: "",
+            address: {
+              street: "",
+              housenumber: "",
+              postalCode: "",
+              place: "",
+            },
+            guardians: [
+              {
+                firstName: "",
+                lastName: "",
+                email: "",
+                mobileNumber: "",
+              },
+            ],
+            email: "",
+            mobileNumber: "",
+          },
+          user: {
+            username: "",
+            email: "",
+            confirmEmail: "",
+            password: "",
+            confirmPassword: "",
+          },
+          school: "",
+          schoolPlace: "",
+          schoolClass: "",
+          payment: {
+            paymentMethod: "",
+            voucherCode: "",
+            amount: "",
+          },
+          medicalInformation: "",
+          comment: "",
+          additionalFields: {},
+          terms: {},
+          retractionConsent: false,
+        },
+      };
+    },
+  };
+  </script>
+  
+  <style></style>
+  
\ No newline at end of file
diff --git a/aleksis/apps/paweljong/frontend/components/event_registration/eventRegistrationMutation.graphql b/aleksis/apps/paweljong/frontend/components/event_registration/eventRegistrationMutation.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..35d25baae72d5cbacf45d97f4fd1360d02ee6685
--- /dev/null
+++ b/aleksis/apps/paweljong/frontend/components/event_registration/eventRegistrationMutation.graphql
@@ -0,0 +1,8 @@
+mutation sendOrder(
+  $event: ID!
+  $eventRegistration: EventRegistrationInputType!
+) {
+  sendEventRegistration(event: $event, eventRegistration: $eventRegistration) {
+    ok
+  }
+}
diff --git a/aleksis/apps/paweljong/frontend/components/event_registration/helpers.graphql b/aleksis/apps/paweljong/frontend/components/event_registration/helpers.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..6c0cf301ca15e2ff74f65cc015a9029b85ff5780
--- /dev/null
+++ b/aleksis/apps/paweljong/frontend/components/event_registration/helpers.graphql
@@ -0,0 +1,19 @@
+query whoAmI {
+  whoAmI {
+    id
+    username
+    person {
+      id
+      firstName
+      lastName
+      guardians {
+        id
+        firstName
+        lastName
+        mobileNumber
+        email
+      }
+      email
+    }
+  }
+}
diff --git a/aleksis/apps/paweljong/frontend/index.js b/aleksis/apps/paweljong/frontend/index.js
index a0891db80299ff99e9d9b10a1cecd7f87d37b7b6..a3229cc26cdc659db587c84943099db4088eb2b5 100644
--- a/aleksis/apps/paweljong/frontend/index.js
+++ b/aleksis/apps/paweljong/frontend/index.js
@@ -30,11 +30,9 @@ export default {
     },
     {
       path: "event/:slug/register/",
-      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+      component: () => import("./components/event_registration/EventRegistrationForm.vue"),
       name: "paweljong.registerEventBySlug",
-      props: {
-        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
-      },
+      props: true,
     },
     {
       path: "group_persons/:pk/add/",
diff --git a/aleksis/apps/paweljong/frontend/messages/en.json b/aleksis/apps/paweljong/frontend/messages/en.json
index 802ccc0578ca5f59c36d54c9e4ec80ffabf1d61f..fcefaff4031799872be90aeb94bb34268137aad4 100644
--- a/aleksis/apps/paweljong/frontend/messages/en.json
+++ b/aleksis/apps/paweljong/frontend/messages/en.json
@@ -51,6 +51,202 @@
         "optional": "Optional"
       },
       "help_text": "Helptext"
+    },
+    "event_registration": {
+      "form": {
+        "submitted": {
+          "thank_you": "Thanks!",
+          "submitted_successfully": "Your registration was submitted successfully."
+        },
+        "steps": {
+          "email": {
+            "title": "E-Mail address",
+            "choose_mode": {
+              "help_text": "To continue, you'll need to provide an e-mail address. You can choose between registering an e-mail address with AlekSIS and using your own, existing e-mail address.",
+              "continue_aleksis": "Continue using new AlekSIS e-mail address",
+              "continue_own": "Continue using own e-mail address"
+            },
+            "help_texts": {
+              "participant": "",
+              "guardian": ""
+            },
+            "fields": {
+              "email": {
+                "label": "E-Mail address"
+              },
+              "confirm_email": {
+                "label": "Confirm e-mail address"
+              }
+            }
+          },
+          "register": {
+            "title": "Account",
+            "help_texts": {
+              "participant": "",
+              "guardian": ""
+            },
+            "fields": {
+              "first_name": {
+                "label": "First name"
+              },
+              "last_name": {
+                "label": "Last name"
+              },
+              "username": {
+                "label": "Username"
+              },
+              "password": {
+                "label": "Password"
+              },
+              "confirm_password": {
+                "label": "Confirm password"
+              }
+            }
+          },
+          "contact_details": {
+            "title": "Contact information",
+            "help_texts": {
+              "participant": "",
+              "guardian": ""
+            },
+            "fields": {
+              "first_name": {
+                "label": "First name"
+              },
+              "last_name": {
+                "label": "Last name"
+              },
+              "date_of_birth": {
+                "label": "Date of birth"
+              },
+              "sex": {
+                "label": "Sex",
+                "help_text": "For various reasons, e.g. because we have to keep gender segregation during the night for legal reasons, we need to know if you are a boy or a girl."
+              },
+              "street": {
+                "label": "Street"
+              },
+              "housenumber": {
+                "label": "Housenumber"
+              },
+              "postal_code": {
+                "label": "Postal code"
+              },
+              "place": {
+                "label": "Place"
+              },
+              "email": {
+                "label": "E-Mail address",
+                "help_text": "Please use your personal e-mail address here, which you will check personally. Important information will always be sent to your parents as well. Do not use an e-mail address owned by your parents here."
+              },
+              "mobile_number": {
+                "label": "Mobile number",
+                "help_text": "Your mobile number helps us to reach you in an emergency during the event, e.g. if you are alone with your group at a conference or similar. If you don't have a cell phone, you can leave the field blank."
+              },
+              "school": {
+                "label": "School",
+                "help_text": "Please enter the name of your school."
+              },
+              "school_place": {
+                "label": "School place",
+                "help_text": "Enter the place (city) where your school is located."
+              },
+              "school_class": {
+                "label": "School class",
+                "help_text": "Please enter the class you are in (e.g. 8a)."
+              }
+            }
+          },
+          "guardians": {
+            "title": "Legal guardians",
+            "counter": "Guardian {i}",
+            "add": "Add guardian",
+            "help_texts": {
+              "participant": "",
+              "guardian": ""
+            },
+            "fields": {
+              "first_name": {
+                "label": "Guardian's first name",
+                "help_text": "Please enter the first name of the legal guardian who will fill in the registration with you and who can be reached during the event in an emergency."
+              },
+              "last_name": {
+                "label": "Guardian's last name",
+                "help_text": "Please enter the last name of the legal guardian who will fill in the registration with you and who can be reached during the event in an emergency."
+              },
+              "email": {
+                "label": "Guardian's email address"
+              },
+              "mobile_number": {
+                "label": "Guardian's mobile number",
+                "help_text": "We need the mobile phone number for emergencies if we urgently need to reach your parents during the event."
+              }
+            }
+          },
+          "additional": {
+            "title": "Additional information",
+            "help_texts": {
+              "participant": "",
+              "guardian": ""
+            },
+            "fields": {
+              "medical_information": {
+                "label": "Medical information / intolerances",
+                "help_text": "If there are any medically important things we need to consider, e.g. when making food or to make sure you take prescribed medication, please enter it here."
+              }
+            }
+          },
+          "financial": {
+            "title": "Payment",
+            "help_texts": {
+              "participant": "",
+              "guardian": ""
+            },
+            "fields": {
+              "": {
+                "": ""
+              }
+            }
+          },
+          "consent": {
+            "title": "Consent",
+            "help_texts": {
+              "participant": "",
+              "guardian": ""
+            },
+            "fields": {
+              "retraction_consent": {
+                "label": "I confirm that the retraction of the registration is not possible anymore after {date}"
+              }
+            }
+          },
+          "confirm": {
+            "title": "Confirm",
+            "hint": "After this step, you won't be able to edit your registration.",
+            "help_texts": {
+              "participant": "",
+              "guardian": ""
+            },
+            "fields": {
+              "comment": {
+                "label": "Other remarks",
+                "help_text": "You can write down any remarks you want to tell us here."
+              }
+            }
+          }
+        },
+        "rules": {
+          "email": {
+            "valid": "This is not a valid e-mail address"
+          },
+          "confirm_email": {
+            "no_match": "The e-mail addresses do not match"
+          },
+          "confirm_password": {
+            "no_match": "The passwords do not match"
+          }
+        }
+      }
     }
   }
 }
diff --git a/aleksis/apps/paweljong/schema/__init__.py b/aleksis/apps/paweljong/schema/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..dd070d2d769444d5722ae6742717f0074ea36f82
--- /dev/null
+++ b/aleksis/apps/paweljong/schema/__init__.py
@@ -0,0 +1,49 @@
+from django.core.exceptions import PermissionDenied
+
+import graphene
+from graphene_django import DjangoObjectType
+from graphql import GraphQLError
+
+from aleksis.core.schema.base import FilterOrderList
+
+from .checkpoint import CheckpointCheckInMutation
+from .event import EventType
+from .event_additional_field import (
+    EventAdditionalFieldBatchCreateMutation,
+    EventAdditionalFieldBatchDeleteMutation,
+    EventAdditionalFieldBatchPatchMutation,
+    EventAdditionalFieldType
+)
+from .terms import TermsType
+from ..models import Event
+
+
+class Query(graphene.ObjectType):
+    event_additional_fields = FilterOrderList(EventAdditionalFieldType)
+
+    event_by_id = graphene.Field(EventType, id=graphene.ID())
+    event_by_slug = graphene.Field(EventType, slug=graphene.String())
+
+    terms = FilterOrderList(TermsType)
+
+    @staticmethod
+    def resolve_event_by_id(root, info, id):
+        event = Event.objects.get(pk=id)
+        # if not info.context.user.has_perm("paweljong.view_person_rule", event):
+        #     return None
+        return event
+
+    @staticmethod
+    def resolve_event_by_slug(root, info, slug):
+        event = Event.objects.get(slug=slug)
+        # if not info.context.user.has_perm("paweljong.view_person_rule", event):
+        #     return None
+        return event
+
+
+class Mutation(graphene.ObjectType):
+    checkpoint_check_in = CheckpointCheckInMutation.Field()
+
+    create_event_additional_fields = EventAdditionalFieldBatchCreateMutation.Field()
+    delete_event_additional_fields = EventAdditionalFieldBatchDeleteMutation.Field()
+    update_event_additional_fields = EventAdditionalFieldBatchPatchMutation.Field()
diff --git a/aleksis/apps/paweljong/schema.py b/aleksis/apps/paweljong/schema/checkpoint.py
similarity index 50%
rename from aleksis/apps/paweljong/schema.py
rename to aleksis/apps/paweljong/schema/checkpoint.py
index 7db42cc294f24a114e055dba9333b26976cd1eee..5c1b03cabd3ad7fa016b38f01626eb11ba79071e 100644
--- a/aleksis/apps/paweljong/schema.py
+++ b/aleksis/apps/paweljong/schema/checkpoint.py
@@ -3,24 +3,12 @@ from django.utils import timezone
 
 import graphene
 from graphene_django import DjangoObjectType
-from graphql import GraphQLError
 
-from aleksis.core.models import Person
-from aleksis.core.schema.base import (
-    BaseBatchCreateMutation,
-    BaseBatchDeleteMutation,
-    BaseBatchPatchMutation,
-    FilterOrderList,
-    PermissionsTypeMixin,
-)
+from aleksis.core.schema.base import PermissionsTypeMixin
 from aleksis.core.util.core_helpers import has_person
+from aleksis.core.models import Person
 
-from .models import Checkpoint, Event, EventAdditionalField
-
-
-class EventType(PermissionsTypeMixin, DjangoObjectType):
-    class Meta:
-        model = Event
+from ..models import Checkpoint, Event
 
 
 class CheckpointType(PermissionsTypeMixin, DjangoObjectType):
@@ -28,11 +16,6 @@ class CheckpointType(PermissionsTypeMixin, DjangoObjectType):
         model = Checkpoint
 
 
-class EventAdditionalFieldType(PermissionsTypeMixin, DjangoObjectType):
-    class Meta:
-        model = EventAdditionalField
-
-
 class CheckpointCheckInMutation(graphene.Mutation):
     class Arguments:
         event_slug = graphene.String(required=True)
@@ -84,46 +67,3 @@ class CheckpointCheckInMutation(graphene.Mutation):
         checkpoint.save()
 
         return CheckpointCheckInMutation(checkpoint=checkpoint)
-
-
-class EventAdditionalFieldBatchCreateMutation(BaseBatchCreateMutation):
-    class Meta:
-        model = EventAdditionalField
-        permissions = ("paweljong.create_event_additional_field_rule",)
-        only_fields = (
-            "title",
-            "field_type",
-            "required",
-            "help_text",
-        )
-
-
-class EventAdditionalFieldBatchDeleteMutation(BaseBatchDeleteMutation):
-    class Meta:
-        model = EventAdditionalField
-        permissions = ("paweljong.delete_event_additional_field_rule",)
-
-
-class EventAdditionalFieldBatchPatchMutation(BaseBatchPatchMutation):
-    class Meta:
-        model = EventAdditionalField
-        permissions = ("paweljong.edit_event_additional_field_rule",)
-        only_fields = (
-            "id",
-            "title",
-            "field_type",
-            "required",
-            "help_text",
-        )
-
-
-class Query(graphene.ObjectType):
-    event_additional_fields = FilterOrderList(EventAdditionalFieldType)
-
-
-class Mutation(graphene.ObjectType):
-    checkpoint_check_in = CheckpointCheckInMutation.Field()
-
-    create_event_additional_fields = EventAdditionalFieldBatchCreateMutation.Field()
-    delete_event_additional_fields = EventAdditionalFieldBatchDeleteMutation.Field()
-    update_event_additional_fields = EventAdditionalFieldBatchPatchMutation.Field()
diff --git a/aleksis/apps/paweljong/schema/event.py b/aleksis/apps/paweljong/schema/event.py
new file mode 100644
index 0000000000000000000000000000000000000000..539969c098b44d87295caa4091ee052eb2b07212
--- /dev/null
+++ b/aleksis/apps/paweljong/schema/event.py
@@ -0,0 +1,10 @@
+from graphene_django import DjangoObjectType
+
+from aleksis.core.schema.base import PermissionsTypeMixin
+
+from ..models import Event
+
+
+class EventType(PermissionsTypeMixin, DjangoObjectType):
+    class Meta:
+        model = Event
diff --git a/aleksis/apps/paweljong/schema/event_additional_field.py b/aleksis/apps/paweljong/schema/event_additional_field.py
new file mode 100644
index 0000000000000000000000000000000000000000..a01142807a10c3c48fe3ef790875a5edeb23fe5d
--- /dev/null
+++ b/aleksis/apps/paweljong/schema/event_additional_field.py
@@ -0,0 +1,46 @@
+from graphene_django import DjangoObjectType
+
+from aleksis.core.schema.base import (
+    BaseBatchCreateMutation,
+    BaseBatchDeleteMutation,
+    BaseBatchPatchMutation,
+    PermissionsTypeMixin,
+)
+
+from ..models import EventAdditionalField
+
+
+class EventAdditionalFieldType(PermissionsTypeMixin, DjangoObjectType):
+    class Meta:
+        model = EventAdditionalField
+
+
+class EventAdditionalFieldBatchCreateMutation(BaseBatchCreateMutation):
+    class Meta:
+        model = EventAdditionalField
+        permissions = ("paweljong.create_event_additional_field_rule",)
+        only_fields = (
+            "title",
+            "field_type",
+            "required",
+            "help_text",
+        )
+
+
+class EventAdditionalFieldBatchDeleteMutation(BaseBatchDeleteMutation):
+    class Meta:
+        model = EventAdditionalField
+        permissions = ("paweljong.delete_event_additional_field_rule",)
+
+
+class EventAdditionalFieldBatchPatchMutation(BaseBatchPatchMutation):
+    class Meta:
+        model = EventAdditionalField
+        permissions = ("paweljong.edit_event_additional_field_rule",)
+        only_fields = (
+            "id",
+            "title",
+            "field_type",
+            "required",
+            "help_text",
+        )
diff --git a/aleksis/apps/paweljong/schema/event_registration.py b/aleksis/apps/paweljong/schema/event_registration.py
new file mode 100644
index 0000000000000000000000000000000000000000..a63bdf92d2ce12e472830f0097df852ddda9a44c
--- /dev/null
+++ b/aleksis/apps/paweljong/schema/event_registration.py
@@ -0,0 +1,26 @@
+import graphene
+from graphene_django import DjangoObjectType
+
+from aleksis.core.schema.base import PermissionsTypeMixin
+
+from ..models import EventRegistration
+
+
+class EventRegistrationType(PermissionsTypeMixin, DjangoObjectType):
+    class Meta:
+        model = EventRegistration
+
+
+class EventRegistrationInputType(graphene.InputObjectType):
+    full_name = graphene.String(required=True)
+    email = graphene.String(required=True)
+    notes = graphene.String(required=False)
+    shipping_option = graphene.ID(required=True)
+    payment_option = graphene.ID(required=True)
+    items = graphene.List(OrderItemInputType, required=True)
+    shipping_full_name = graphene.String(required=False)
+    second_address_row = graphene.String(required=False)
+    street = graphene.String(required=False)
+    housenumber = graphene.String(required=False)
+    plz = graphene.String(required=False)
+    place = graphene.String(required=False)
diff --git a/aleksis/apps/paweljong/schema/terms.py b/aleksis/apps/paweljong/schema/terms.py
new file mode 100644
index 0000000000000000000000000000000000000000..0db8dc2a630535266d86b768c8a619fe9091cc48
--- /dev/null
+++ b/aleksis/apps/paweljong/schema/terms.py
@@ -0,0 +1,10 @@
+from graphene_django import DjangoObjectType
+
+from aleksis.core.schema.base import PermissionsTypeMixin
+
+from ..models import Terms
+
+
+class TermsType(PermissionsTypeMixin, DjangoObjectType):
+    class Meta:
+        model = Terms