diff --git a/packages/person-select/README.md b/packages/person-select/README.md
index 6bb84d46edef77a245656e6dc98cdc7719a716b0..3b36e90ea297205f1db4dcdf99a9ba2e54a000e1 100644
--- a/packages/person-select/README.md
+++ b/packages/person-select/README.md
@@ -44,6 +44,13 @@ Or directly via CDN:
     - example auth property: `{token: "THE_BEARER_TOKEN"}`
     - note: most often this should be an attribute that is not set directly, but subscribed at a provider
 
+## Override Properties
+
+- `buildUrlData` - A function which gets passed the select and the current search context and
+  should return the query parameters used for searching.
+- `formatPerson` - A function which takes the select and a person object and should
+  return the text representation displayed to the user.
+
 ## Local development
 
 ```bash
diff --git a/packages/person-select/src/person-select.js b/packages/person-select/src/person-select.js
index f56fc9b617777aa8e82019e99188081c97d9e5fa..4a0c3b5755de12e967dc447abe74bd3259e72476 100644
--- a/packages/person-select/src/person-select.js
+++ b/packages/person-select/src/person-select.js
@@ -178,10 +178,8 @@ export class PersonSelect extends ScopedElementsMixin(AdapterLitElement) {
                         jqXHR.setRequestHeader('Authorization', 'Bearer ' + that.auth.token);
                         that.isSearching = true;
                     },
-                    data: function (params) {
-                        return {
-                            search: params.term.trim(),
-                        };
+                    data: (params) => {
+                        return this.buildUrlData(this, params);
                     },
                     processResults: function (data) {
                         that.$('#person-select-dropdown').addClass('select2-bug');
@@ -192,7 +190,7 @@ export class PersonSelect extends ScopedElementsMixin(AdapterLitElement) {
                         transformed.forEach((person) => {
                             results.push({
                                 id: person['@id'],
-                                text: that.generateOptionText(person),
+                                text: that.formatPerson(that, person),
                             });
                         });
 
@@ -267,7 +265,7 @@ export class PersonSelect extends ScopedElementsMixin(AdapterLitElement) {
                     const identifier = transformed['@id'];
 
                     const option = new Option(
-                        that.generateOptionText(transformed),
+                        that.formatPerson(this, transformed),
                         identifier,
                         true,
                         true
@@ -295,7 +293,29 @@ export class PersonSelect extends ScopedElementsMixin(AdapterLitElement) {
         return true;
     }
 
-    generateOptionText(person) {
+    /**
+     * Gets passed the select2 params (https://select2.org/data-sources/ajax#jquery-ajax-options)
+     * and should return an object containing the query parameters send to the server.
+     *
+     * @param {object} select
+     * @param {object} params
+     * @returns {object}
+     */
+    buildUrlData(select, params) {
+        return {
+            search: params.term.trim(),
+        };
+    }
+
+    /**
+     * Gets passed a person object and should return a string representation that will
+     * will be shown to the user.
+     *
+     * @param {object} select
+     * @param {object} person
+     * @returns {string}
+     */
+    formatPerson(select, person) {
         let text = person['givenName'] ?? '';
         if (person['familyName']) {
             text += ` ${person['familyName']}`;