diff --git a/docs/docs/core/content-manager/relations.mdx b/docs/docs/core/content-manager/relations.mdx
new file mode 100644
index 0000000000..8a080eacda
--- /dev/null
+++ b/docs/docs/core/content-manager/relations.mdx
@@ -0,0 +1,144 @@
+---
+title: Relations
+slug: /content-manager/relations
+description: Conceptual guide to relations in the Content Manager focussing on the technical decisions taken.
+tags:
+ - content-manager
+ - relations
+ - redux-store
+---
+
+## Summary
+
+Relations are a term used to describe how two or more entities are connected. Previously in the sidebar of an entity,
+in Nov2020 we released a refactor that moved these fields into the main editing flow for a better editor experience
+and to improve performance of the CMS application when many relations were used.
+
+
+
+_above: An example of the relations input in the CMS edit view_
+
+## Data management in frontend
+
+
+
+_above: A high-level diagram of how relations state management works_
+
+### Preparing relation fields in the store
+
+When you first open an existing entity, we call the admin API and put the data into the store to pre-populate fields
+with existing values. However, its important to know when you have fields with `type === 'relation'` in your schema
+that the data you receive will not be an array, but rather an object with the count of how many relations in that
+field exist. For example, a section of the response may look like this:
+
+```json
+{
+ "my_relations": {
+ "count": 6
+ }
+}
+```
+
+So without intervention, your inputs would try to append new relations to the `my_relations` object, which would not
+work. Instead of this, before calling the redux action `INIT_FORM` we recursively find the paths fields based on the
+following conditions:
+
+- The field is a relation
+- The field is a component
+- The field is a repeatable component
+- The field is a dynamic zone
+
+These paths _do not_ take into account index values. So if you have a repetable component field where the schema looks like:
+
+```json
+{
+ "repeatable_single_component_relation": {
+ "type": "component",
+ "repeatable": true,
+ "component": "basic.relation"
+ }
+}
+```
+
+and the components looks like:
+
+```json
+{
+ "basic.relation": {
+ "attributes": {
+ "id": {
+ "type": "integer"
+ },
+ "categories": {
+ "type": "relation",
+ "relation": "oneToMany",
+ "target": "api::category.category",
+ "targetModel": "api::category.category",
+ "relationType": "oneToMany"
+ },
+ "my_name": {
+ "type": "string"
+ }
+ }
+ }
+}
+```
+
+Then the path to the relation field would be `repeatable_single_component_relation.categories`. Even though when
+relations are added the path to the field in the redux store would be `repeatable_single_component_relation.0.categories`.
+
+Inside the reducer we reduce the array of `relationalFieldPaths` to an object with the `initialValues` clone as
+as the base. If there is `modifiedData` in the browser i.e. you've made changes to the entity and saved those changes,
+we just replace the first level of the field with the `modifiedData` so the data structure is preserved and we're not
+loosing the relations we had already loaded in the component. If the first part of the path is highlighted as the
+`relationalField` then we simply replace that intial object with an empty array.
+
+However, if the first part of the path is either a repeatable component, a dynamic zone or a regular component then we
+recursively find the relation fields and replace the object with an array. This is handled by the `findLeafByPathAndReplace`
+utility function. This function in short, takes an end path (in this case the relational field) and a primitive to replace
+when it finds the endpath (an empty array in this case). It then recursively reduces the paths to the relational field mapping
+through arrays if necessary (in the instance of repetable components for example) replacing the endpath with the primitive.
+
+When this is done, we have sucessfully prepared our initial data for usage with relations.
+
+### Handling updates to relation fields
+
+Because we've prepared the fields prior to the component loading, adding & removing relations, it's relatively easy to do so.
+When a relation is added, we simply push the new relation to the array of relations. When a relation is removed, we simply
+filter out the relation from the array of relations. This is handled inside the reducer actions `CONNECT_RELATION` &
+`DISCONNECT_RELATION` respectively.
+
+:::note
+Connecting relations adds the item to the end of the list, whilst loading more relations prepends to
+the beginning of the list. This is the expected behaviour.
+:::
+
+The `RelationInput` component takes the field in `modifiedData` as its source of truth. You could therefore consider this to
+be the `browserState` and `initialData` to be the `serverState`. When relations are loaded they're added to both the `intialData`
+and `modifiedData` objects, but when you connect/disconnect only the `modifiedData` is updated. This is useful when we're preparing
+data for the api.
+
+### Cleaning data to be posted to the API
+
+The API to update the enttiy expects relations to be categorised into two groups, a `connect` array and `disconnect` array.
+You could do this as the user interacts with the input but we found this to be confusing and then involved us managing three
+different arrays which makes the code more complex. Instead, because the browser doesn't really care about whats new and removed
+and we have a copy of the slice of data we're mutating from the server we can run a small diff algorithm to determine which
+relations have been connected and which have been disconnected. Returning an object like so:
+
+```json
+{
+ "my_relations": {
+ "connect": [{ "id": 1 }, { "id": 2 }],
+ "disconnect": []
+ }
+}
+```
+
+## Frontend component architecture
diff --git a/docs/sidebars.js b/docs/sidebars.js
index 24047f984b..c64e6f077a 100644
--- a/docs/sidebars.js
+++ b/docs/sidebars.js
@@ -31,7 +31,13 @@ const sidebars = {
type: 'doc',
id: 'core/content-manager/intro',
},
- items: ['example'],
+ items: [
+ {
+ type: 'doc',
+ label: 'Relations',
+ id: 'core/content-manager/relations',
+ },
+ ],
},
{
type: 'category',
diff --git a/docs/static/img/content-manager/relations/component-example.png b/docs/static/img/content-manager/relations/component-example.png
new file mode 100644
index 0000000000..71630c2142
Binary files /dev/null and b/docs/static/img/content-manager/relations/component-example.png differ
diff --git a/docs/static/img/content-manager/relations/relations-statemanagemen-diagram.png b/docs/static/img/content-manager/relations/relations-statemanagemen-diagram.png
new file mode 100644
index 0000000000..7092b517f4
Binary files /dev/null and b/docs/static/img/content-manager/relations/relations-statemanagemen-diagram.png differ