diff --git a/aleksis/apps/postbuero/frontend/components/injectables/MailAddressFormStep.vue b/aleksis/apps/postbuero/frontend/components/injectables/MailAddressFormStep.vue
new file mode 100644
index 0000000000000000000000000000000000000000..762db6e9fa40e19ea55dd08d244589a50fa42d69
--- /dev/null
+++ b/aleksis/apps/postbuero/frontend/components/injectables/MailAddressFormStep.vue
@@ -0,0 +1,175 @@
+<template>
+  <v-form :value="value" @input="$emit('input', getFormStepStatus($event))">
+    <v-tabs
+      v-model="emailMode"
+      :grow="!$vuetify.breakpoint.mdAndDown"
+      optional
+      show-arrows
+    >
+      <v-tab style="max-width: 50vw" class="primary--text">
+        {{ $t("postbuero.mail_addresses.form_step.mode.new") }}
+      </v-tab>
+      <v-tab style="max-width: 50vw" class="primary--text">
+        {{ $t("postbuero.mail_addresses.form_step.mode.existing") }}
+      </v-tab>
+    </v-tabs>
+    <v-tabs-items v-model="emailMode">
+      <v-tab-item>
+        <v-row class="mt-4">
+          <v-col cols="12" md="6">
+            <div aria-required="true">
+              <v-text-field
+                outlined
+                v-model="data.mailAddress.localPart"
+                :label="$t('postbuero.mail_addresses.data_table.local_part')"
+                :rules="
+                  emailMode === 0
+                    ? $rules().required.build(rules.emailLocalPart)
+                    : []
+                "
+                required
+              ></v-text-field>
+            </div>
+          </v-col>
+          <v-col cols="12" md="6">
+            <div aria-required="true">
+              <v-autocomplete
+                outlined
+                hide-no-data
+                :items="mailDomains"
+                item-text="domain"
+                item-value="id"
+                :loading="$apollo.queries.mailDomains.loading"
+                prepend-icon="mdi-at"
+                v-model="data.mailAddress.domain"
+                :label="$t('postbuero.mail_addresses.data_table.domain')"
+                required
+                :rules="emailMode === 0 ? $rules().required.build() : []"
+              />
+            </div>
+          </v-col>
+        </v-row>
+      </v-tab-item>
+      <v-tab-item>
+        <v-row class="mt-4">
+          <v-col cols="12" md="6">
+            <div aria-required="true">
+              <v-text-field
+                outlined
+                v-model="data.accountRegistration.user.email"
+                :label="
+                  $t('postbuero.mail_addresses.form_step.fields.email.label')
+                "
+                required
+                :rules="
+                  emailMode === 1 ? $rules().required.isEmail.build() : []
+                "
+                prepend-icon="mdi-email-outline"
+              ></v-text-field>
+            </div>
+          </v-col>
+          <v-col cols="12" md="6">
+            <div aria-required="true">
+              <v-text-field
+                outlined
+                v-model="confirmFields.confirmEmail"
+                :label="
+                  $t(
+                    'postbuero.mail_addresses.form_step.fields.confirm_email.label',
+                  )
+                "
+                required
+                :rules="
+                  emailMode === 1
+                    ? $rules().required.build(rules.confirmEmail)
+                    : []
+                "
+                prepend-icon="mdi-email-outline"
+              ></v-text-field>
+            </div>
+          </v-col>
+        </v-row>
+      </v-tab-item>
+    </v-tabs-items>
+  </v-form>
+</template>
+
+<script>
+import {
+  mailDomainsForUser,
+  disallowedLocalParts,
+} from "../mail_addresses/mailAddresses.graphql";
+
+import formRulesMixin from "aleksis.core/mixins/formRulesMixin";
+
+export default {
+  name: "MailAddressFormStep",
+  apollo: {
+    mailDomains: mailDomainsForUser,
+    disallowedLocalParts: disallowedLocalParts,
+  },
+  mixins: [formRulesMixin],
+  emits: ["dataChange", "emailModeChange"],
+  props: {
+    value: {
+      type: Boolean,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      data: {
+        mailAddress: {
+          localPart: "",
+          domain: "",
+        },
+        accountRegistration: {
+          user: {
+            email: "",
+          },
+        },
+      },
+      confirmFields: {
+        email: "",
+      },
+      emailMode: null,
+    };
+  },
+  computed: {
+    rules() {
+      return {
+        confirmEmail: [
+          (v) =>
+            this.data.accountRegistration.user.email == v ||
+            this.$t("accounts.signup.form.rules.confirm_email.no_match"),
+        ],
+        emailLocalPart: [
+          (v) =>
+            /^\w+([.!#$%&'*+-\/=?^_`{|}~]?\w+)*$/.test(v) ||
+            this.$t(
+              "postbuero.mail_addresses.data_table.errors.local_part_invalid_characters",
+            ),
+          (v) =>
+            this.disallowedLocalParts.indexOf(v) === -1 ||
+            this.$t(
+              "postbuero.mail_addresses.data_table.errors.local_part_disallowed",
+            ),
+        ],
+      };
+    },
+  },
+  methods: {
+    getFormStepStatus(formStatus) {
+      return this.emailMode !== null && formStatus;
+    },
+  },
+  watch: {
+    data: {
+      deep: true,
+      handler(newValue) {
+        this.$emit("dataChange", newValue);
+      },
+    },
+  },
+};
+</script>
diff --git a/aleksis/apps/postbuero/frontend/components/injectables/registerMailAddress.graphql b/aleksis/apps/postbuero/frontend/components/injectables/registerMailAddress.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..461aada433f3f913b6144bb89d57972fdffcf99b
--- /dev/null
+++ b/aleksis/apps/postbuero/frontend/components/injectables/registerMailAddress.graphql
@@ -0,0 +1,7 @@
+mutation registerMailAddress(
+  $mailAddress: MailAddressInputType
+) {
+  registerMailAddress(mailAddress: $mailAddress) {
+    ok
+  }
+}
diff --git a/aleksis/apps/postbuero/frontend/index.js b/aleksis/apps/postbuero/frontend/index.js
index a6e922fc6052b2aa2ef3276b6087a78b7b322232..7db23ffe7f3078fce30ea534a1867b579d4dcf78 100644
--- a/aleksis/apps/postbuero/frontend/index.js
+++ b/aleksis/apps/postbuero/frontend/index.js
@@ -3,6 +3,24 @@ import {
   hasPersonValidator,
 } from "aleksis.core/routeValidators";
 
+import { registerMailAddress } from "./components/injectables/registerMailAddress.graphql";
+
+export const collectionItems = {
+  coreAccountRegistrationSteps: [
+    {
+      key: "email",
+      component: () =>
+        import("./components/injectables/MailAddressFormStep.vue"),
+    },
+  ],
+  coreAccountRegistrationExtraMutations: [
+    {
+      key: "email",
+      mutation: registerMailAddress,
+    },
+  ],
+};
+
 export default {
   meta: {
     inMenu: true,
diff --git a/aleksis/apps/postbuero/frontend/messages/en.json b/aleksis/apps/postbuero/frontend/messages/en.json
index d955d5ab809acca9dacfb63811b2e5f984676512..227ad28286c8692856a4affe49204f37fea3a546 100644
--- a/aleksis/apps/postbuero/frontend/messages/en.json
+++ b/aleksis/apps/postbuero/frontend/messages/en.json
@@ -13,7 +13,21 @@
           "local_part_disallowed": "This local part name is disallowed."
         }
       },
-      "create": "Create new mail address"
+      "create": "Create new mail address",
+      "form_step": {
+        "mode": {
+          "new": "New email address",
+          "existing": "Existing email address"
+        },
+        "fields": {
+          "email": {
+            "label": "E-Mail address"
+          },
+          "confirm_email": {
+            "label": "Confirm e-mail address"
+          }
+        }
+      }
     },
     "mail_domains": {
       "title_plural": "Manage mail domains",
diff --git a/aleksis/apps/postbuero/schema.py b/aleksis/apps/postbuero/schema.py
index 0fc7c5b8c140062e31354ac7a23bfc556fd80776..1502cd04d36aa2131ed4b44ea06983fe0398753a 100644
--- a/aleksis/apps/postbuero/schema.py
+++ b/aleksis/apps/postbuero/schema.py
@@ -1,5 +1,8 @@
+from typing import Optional
+
 from django.conf import settings
-from django.core.exceptions import PermissionDenied
+from django.core.exceptions import PermissionDenied, ValidationError
+from django.db import IntegrityError
 from django.utils.translation import gettext_lazy as _
 
 import graphene
@@ -113,6 +116,59 @@ class MailDomainBatchPatchMutation(BaseBatchPatchMutation):
         permissions = ("postbuero.change_maildomain",)
 
 
+class MailAddressRegistrationMutation(graphene.Mutation):
+    class Arguments:
+        mail_address = MailAddressInputType(required=False)
+
+    ok = graphene.Boolean()
+
+    @classmethod
+    def mutate(cls, root, info, mail_address: Optional[MailAddressInputType] = None):
+        if not hasattr(info.context, "_registering_person"):
+            raise PermissionDenied(_("This mutation may only be used during account registration."))
+
+        person = getattr(info.context, "_registering_person", None)
+        if person is None:
+            return MailAddressRegistrationMutation(ok=False)
+
+        if mail_address is not None and mail_address["domain"] is not None and mail_address["domain"] != "" and mail_address["local_part"] is not None and mail_address["local_part"] != "":
+            try:
+                domain = MailDomain.objects.get(pk=mail_address.domain)
+            except IntegrityError as exc:
+                raise ValidationError(_("Mail domain does not exist.")) from exc
+            try:
+                _mail_address = MailAddress.objects.create(
+                    local_part=mail_address.local_part,
+                    domain=domain,
+                )
+            except IntegrityError as exc:
+                raise ValidationError(_("Mail address already in use.")) from exc
+
+            _mail_address.person = person
+            _mail_address.save()
+
+            try:
+                person.email = str(_mail_address)
+                person.save()
+            except IntegrityError as exc:
+                raise ValidationError(_("Person with this mail address already exists.")) from exc
+            
+            user = person.user
+
+            try:
+                user.email = str(_mail_address)
+                user.save()
+            except IntegrityError as exc:
+                raise ValidationError(_("User with this mail address already exists.")) from exc
+            
+            return MailAddressRegistrationMutation(ok=True)
+        elif hasattr(person, "email") and person.email != "":
+            # If person already has email set, not registering an address here is fine.
+            return MailAddressRegistrationMutation(ok=True)
+        else:
+            raise ValidationError(_("Mail address is required."))
+
+
 class Query(graphene.ObjectType):
     mail_addresses_for_user = FilterOrderList(MailAddressType)
 
@@ -163,3 +219,5 @@ class Mutation(graphene.ObjectType):
     create_mail_domains = MailDomainBatchCreateMutation.Field()
     delete_mail_domains = MailDomainBatchDeleteMutation.Field()
     patch_mail_domains = MailDomainBatchPatchMutation.Field()
+
+    register_mail_address = MailAddressRegistrationMutation.Field()