diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue
index 4222ffe42fe739fd3b7b5d637e13512b11fe0a6d..842c110e896c23b5941f724e2746e677b5b426a6 100644
--- a/app/assets/javascripts/work_items/components/work_item_detail.vue
+++ b/app/assets/javascripts/work_items/components/work_item_detail.vue
@@ -6,6 +6,7 @@ import workItemTitleSubscription from '../graphql/work_item_title.subscription.g
 import WorkItemActions from './work_item_actions.vue';
 import WorkItemState from './work_item_state.vue';
 import WorkItemTitle from './work_item_title.vue';
+import WorkItemLinks from './work_item_links/work_item_links.vue';
 
 export default {
   i18n,
@@ -15,6 +16,7 @@ export default {
     WorkItemActions,
     WorkItemTitle,
     WorkItemState,
+    WorkItemLinks,
   },
   props: {
     workItemId: {
@@ -105,6 +107,7 @@ export default {
         @error="error = $event"
         @updated="$emit('workItemUpdated')"
       />
+      <work-item-links :work-item-id="workItem.id" />
     </template>
   </section>
 </template>
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue
new file mode 100644
index 0000000000000000000000000000000000000000..6124e669cb32af6bb1e0a7f1eca47ed0dbc6c767
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue
@@ -0,0 +1,86 @@
+<script>
+import { GlButton } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import WorkItemLinksForm from './work_item_links_form.vue';
+
+export default {
+  components: {
+    GlButton,
+    WorkItemLinksForm,
+  },
+  props: {
+    workItemId: {
+      type: String,
+      required: false,
+      default: null,
+    },
+  },
+  data() {
+    return {
+      isShownAddForm: false,
+      isOpen: false,
+      children: [],
+    };
+  },
+  computed: {
+    // Only used for children for now but should be extended later to support parents and siblings
+    isChildrenEmpty() {
+      return this.children.length === 0;
+    },
+    toggleIcon() {
+      return this.isOpen ? 'angle-up' : 'angle-down';
+    },
+  },
+  methods: {
+    toggle() {
+      this.isOpen = !this.isOpen;
+    },
+    toggleAddForm() {
+      this.isShownAddForm = !this.isShownAddForm;
+    },
+  },
+  i18n: {
+    title: s__('WorkItem|Child items'),
+    emptyStateMessage: s__(
+      'WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!',
+    ),
+    addChildButtonLabel: s__('WorkItem|Add a child'),
+  },
+};
+</script>
+
+<template>
+  <div class="gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100">
+    <div
+      class="gl-p-4 gl-display-flex gl-justify-content-space-between"
+      :class="{ 'gl-border-b-1 gl-border-b-solid gl-border-b-gray-100': isOpen }"
+    >
+      <h5 class="gl-m-0 gl-line-height-32">{{ $options.i18n.title }}</h5>
+      <div class="gl-border-l-1 gl-border-l-solid gl-border-l-gray-50 gl-pl-4">
+        <gl-button
+          category="tertiary"
+          :icon="toggleIcon"
+          data-testid="toggle-links"
+          @click="toggle"
+        />
+      </div>
+    </div>
+    <div v-if="isOpen" class="gl-bg-gray-10 gl-p-4" data-testid="links-body">
+      <div v-if="isChildrenEmpty" class="gl-px-8" data-testid="links-empty">
+        <p>
+          {{ $options.i18n.emptyStateMessage }}
+        </p>
+        <gl-button
+          v-if="!isShownAddForm"
+          category="secondary"
+          variant="confirm"
+          data-testid="toggle-add-form"
+          @click="toggleAddForm"
+        >
+          {{ $options.i18n.addChildButtonLabel }}
+        </gl-button>
+        <work-item-links-form v-else data-testid="add-links-form" @cancel="toggleAddForm" />
+      </div>
+    </div>
+  </div>
+</template>
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue
new file mode 100644
index 0000000000000000000000000000000000000000..22728f580261e2bfa4c6b8e4cbafdcee141e7004
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue
@@ -0,0 +1,28 @@
+<script>
+import { GlForm, GlFormInput, GlButton } from '@gitlab/ui';
+
+export default {
+  components: {
+    GlForm,
+    GlFormInput,
+    GlButton,
+  },
+  data() {
+    return {
+      relatedWorkItem: '',
+    };
+  },
+};
+</script>
+
+<template>
+  <gl-form @submit.prevent>
+    <gl-form-input v-model="relatedWorkItem" class="gl-mb-4" />
+    <gl-button type="submit" category="secondary" variant="confirm">
+      {{ s__('WorkItem|Add') }}
+    </gl-button>
+    <gl-button category="tertiary" class="gl-float-right" @click="$emit('cancel')">
+      {{ s__('WorkItem|Cancel') }}
+    </gl-button>
+  </gl-form>
+</template>
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 796c77a601ab8b0081dcdd5eba3d5c8a5154d2d7..f3c24b16f1e30a32a6dc1a206cfa1829df7b9586 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -43092,9 +43092,21 @@ msgstr ""
 msgid "Work in progress Limit"
 msgstr ""
 
+msgid "WorkItem|Add"
+msgstr ""
+
+msgid "WorkItem|Add a child"
+msgstr ""
+
 msgid "WorkItem|Are you sure you want to delete the work item? This action cannot be reversed."
 msgstr ""
 
+msgid "WorkItem|Cancel"
+msgstr ""
+
+msgid "WorkItem|Child items"
+msgstr ""
+
 msgid "WorkItem|Convert to work item"
 msgstr ""
 
@@ -43107,6 +43119,9 @@ msgstr ""
 msgid "WorkItem|New Task"
 msgstr ""
 
+msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgstr ""
+
 msgid "WorkItem|Select type"
 msgstr ""
 
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..ee5937ab7e714ed9d5b8a5b79cd4d6aa2e57dfd9
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
@@ -0,0 +1,65 @@
+import { nextTick } from 'vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import WorkItemLinks from '~/work_items/components/work_item_links/work_item_links.vue';
+
+describe('WorkItemLinks', () => {
+  let wrapper;
+
+  const createComponent = () => {
+    wrapper = shallowMountExtended(WorkItemLinks, { propsData: { workItemId: '123' } });
+  };
+
+  const findToggleButton = () => wrapper.findByTestId('toggle-links');
+  const findLinksBody = () => wrapper.findByTestId('links-body');
+  const findEmptyState = () => wrapper.findByTestId('links-empty');
+  const findToggleAddFormButton = () => wrapper.findByTestId('toggle-add-form');
+  const findAddLinksForm = () => wrapper.findByTestId('add-links-form');
+
+  beforeEach(() => {
+    createComponent();
+  });
+
+  afterEach(() => {
+    wrapper.destroy();
+  });
+
+  it('is collapsed by default', () => {
+    expect(findToggleButton().props('icon')).toBe('angle-down');
+    expect(findLinksBody().exists()).toBe(false);
+  });
+
+  it('expands on click toggle button', async () => {
+    findToggleButton().vm.$emit('click');
+    await nextTick();
+
+    expect(findToggleButton().props('icon')).toBe('angle-up');
+    expect(findLinksBody().exists()).toBe(true);
+  });
+
+  it('displays empty state if there are no links', async () => {
+    findToggleButton().vm.$emit('click');
+    await nextTick();
+
+    expect(findEmptyState().exists()).toBe(true);
+    expect(findToggleAddFormButton().exists()).toBe(true);
+  });
+
+  describe('add link form', () => {
+    it('displays form on click add button and hides form on cancel', async () => {
+      findToggleButton().vm.$emit('click');
+      await nextTick();
+
+      expect(findEmptyState().exists()).toBe(true);
+
+      findToggleAddFormButton().vm.$emit('click');
+      await nextTick();
+
+      expect(findAddLinksForm().exists()).toBe(true);
+
+      findAddLinksForm().vm.$emit('cancel');
+      await nextTick();
+
+      expect(findAddLinksForm().exists()).toBe(false);
+    });
+  });
+});