diff --git a/app/assets/javascripts/entrypoints/super_sidebar.js b/app/assets/javascripts/entrypoints/super_sidebar.js
index 6e88a9980969357038bb05c33d0657e37d014bcb..06a34197faf6336ae0e37872b100ff9506665b47 100644
--- a/app/assets/javascripts/entrypoints/super_sidebar.js
+++ b/app/assets/javascripts/entrypoints/super_sidebar.js
@@ -1,6 +1,11 @@
 import '~/webpack';
 import '~/commons';
-import { initSuperSidebar, initSuperSidebarToggle } from '~/super_sidebar/super_sidebar_bundle';
+import {
+  initSuperSidebar,
+  initSuperSidebarToggle,
+  initPageBreadcrumbs,
+} from '~/super_sidebar/super_sidebar_bundle';
 
 initSuperSidebar();
 initSuperSidebarToggle();
+initPageBreadcrumbs();
diff --git a/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js b/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js
index 9ecbc8615bed1d748e52d14dbe60cdd1ab35df47..66d7284744ddfa14ba5e4a6a6f2a07a57a8c3ded 100644
--- a/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js
+++ b/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js
@@ -1,5 +1,5 @@
 import Vue from 'vue';
-import { GlToast } from '@gitlab/ui';
+import { GlBreadcrumb, GlToast } from '@gitlab/ui';
 import VueApollo from 'vue-apollo';
 import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils';
 import createDefaultClient from '~/lib/graphql';
@@ -167,3 +167,24 @@ export const initSuperSidebarToggle = () => {
     },
   });
 };
+
+export function initPageBreadcrumbs() {
+  const el = document.querySelector('#js-vue-page-breadcrumbs');
+  if (!el) return false;
+  const { breadcrumbsJson } = el.dataset;
+
+  const props = {
+    items: JSON.parse(breadcrumbsJson),
+  };
+
+  return new Vue({
+    el,
+    render(h) {
+      return h(GlBreadcrumb, {
+        props,
+        attrs: { 'data-testid': 'breadcrumb-links' },
+        class: 'gl-flex-grow-1',
+      });
+    },
+  });
+}
diff --git a/app/components/pajamas/avatar_component.rb b/app/components/pajamas/avatar_component.rb
index 6baa085e3b47b4cfed9c6dda1a1d15040d54a524..f7bf9cd04c79abad1559e2a7e91f3e8c9abd9355 100644
--- a/app/components/pajamas/avatar_component.rb
+++ b/app/components/pajamas/avatar_component.rb
@@ -9,8 +9,8 @@ def name
   class AvatarComponent < Pajamas::Component
     include Gitlab::Utils::StrongMemoize
 
-    # @param item [User, Project, Group, AvatarEmail]
-    # @param alt [String] text for the alt tag
+    # @param item [User, Project, Group, AvatarEmail, String]
+    # @param alt [String] text for the alt attribute
     # @param class [String] custom CSS class(es)
     # @param size [Integer] size in pixel
     # @param [Hash] avatar_options
@@ -28,8 +28,11 @@ def initialize(item, alt: nil, class: "", size: 64, avatar_options: {})
 
     def avatar_classes
       classes = ["gl-avatar", "gl-avatar-s#{@size}", @class]
-      classes.push("gl-avatar-circle") if @item.is_a?(User) || @item.is_a?(AvatarEmail)
-      classes.push("gl-rounded-base!") if @item.is_a?(Project) || @item.is_a?(Group)
+      if @item.is_a?(User) || @item.is_a?(AvatarEmail)
+        classes.push("gl-avatar-circle")
+      else
+        classes.push("gl-rounded-base!")
+      end
 
       unless src
         classes.push("gl-avatar-identicon")
@@ -41,7 +44,9 @@ def avatar_classes
 
     def src
       strong_memoize(:src) do
-        if @item.is_a?(User)
+        if @item.is_a?(String)
+          @item
+        elsif @item.is_a?(User)
           # Users show a gravatar instead of an identicon. Also avatars of
           # blocked users are only shown if the current_user is an admin.
           # To not duplicate this logic, we are using existing helpers here.
@@ -67,7 +72,7 @@ def srcset
     end
 
     def alt
-      @alt || @item.name
+      @alt || @item.try(:name)
     end
 
     def initial
diff --git a/app/helpers/breadcrumbs_helper.rb b/app/helpers/breadcrumbs_helper.rb
index 47f0d19bd3405fecb7422d4f3e414ebc4b7b9935..7026063df44a9d7a31dc4adb8bed811799f7b2d5 100644
--- a/app/helpers/breadcrumbs_helper.rb
+++ b/app/helpers/breadcrumbs_helper.rb
@@ -31,26 +31,31 @@ def add_to_breadcrumb_collapsed_links(link, location: :before)
     @breadcrumb_collapsed_links[location] << link
   end
 
-  def push_to_schema_breadcrumb(text, link, avatar = nil)
-    list_item = schema_list_item(text, link, schema_breadcrumb_list.size + 1, avatar)
-
-    schema_breadcrumb_list.push(list_item)
+  def push_to_schema_breadcrumb(text, href, avatar = nil)
+    schema_breadcrumb_list.push({ text: text, href: href, avatar: avatar })
   end
 
   def schema_breadcrumb_json
     {
       '@context': 'https://schema.org',
       '@type': 'BreadcrumbList',
-      'itemListElement': build_item_list_elements&.map { |item| item.except('avatar') }
+      'itemListElement': build_item_list_elements&.map&.with_index do |item, index|
+        {
+          '@type' => 'ListItem',
+          'position' => index + 1,
+          'name' => item[:text],
+          'item' => ensure_absolute_url(item[:href])
+        }
+      end
     }.to_json
   end
 
   def breadcrumbs_as_json
     schema_breadcrumb_list.map do |breadcrumb|
       {
-        text: breadcrumb['name'],
-        href: breadcrumb['item'],
-        avatarPath: breadcrumb['avatar']
+        text: breadcrumb[:text],
+        href: breadcrumb[:href],
+        avatarPath: breadcrumb[:avatar]
       }
     end.to_json
   end
@@ -62,29 +67,25 @@ def schema_breadcrumb_list
   end
 
   def build_item_list_elements
-    return @schema_breadcrumb_list unless @breadcrumbs_extra_links&.any?
-
     last_element = schema_breadcrumb_list.pop
 
-    @breadcrumbs_extra_links.each do |el|
-      push_to_schema_breadcrumb(el[:text], el[:link])
+    if @breadcrumbs_extra_links&.any?
+      @breadcrumbs_extra_links.each do |el|
+        push_to_schema_breadcrumb(el[:text], el[:link])
+      end
     end
 
-    last_element['position'] = schema_breadcrumb_list.last['position'] + 1
-    schema_breadcrumb_list.push(last_element)
-  end
+    if @breadcrumb_collapsed_links&.[](:after)&.any?
+      @breadcrumb_collapsed_links[:after].each do |el|
+        push_to_schema_breadcrumb(el[:text], el[:href], el[:avatar_url])
+      end
+    end
 
-  def schema_list_item(text, link, position, avatar = nil)
-    {
-      '@type' => 'ListItem',
-      'position' => position,
-      'name' => text,
-      'item' => ensure_absolute_link(link),
-      'avatar' => avatar
-    }
+    schema_breadcrumb_list.push(last_element) if last_element
+    schema_breadcrumb_list
   end
 
-  def ensure_absolute_link(link)
+  def ensure_absolute_url(link)
     url = URI.parse(link)
     url.absolute? ? link : URI.join(request.base_url, link).to_s
   rescue URI::InvalidURIError
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index cb600f5a03b30dd8e93b472a69b25fff4bc7e5ae..1e3009d2e8cb23df1d9d30131b51f7136bc9d8a3 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -54,7 +54,10 @@ def group_title(group)
 
     sorted_ancestors(group).with_route.reverse_each.with_index do |parent, index|
       if index > 0
-        add_to_breadcrumb_collapsed_links(group_title_link(parent), location: :before)
+        add_to_breadcrumb_collapsed_links(
+          { text: simple_sanitize(parent.name), href: group_path(parent), avatar_url: parent.try(:avatar_url) },
+          location: :before
+        )
       else
         full_title << breadcrumb_list_item(group_title_link(parent, hidable: false))
       end
diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb
index 9b7171b9842bd084e4519ee70698a0c1f50ec8bc..32643976df6bc245ccec7bfeaec17d0044b13b54 100644
--- a/app/helpers/wiki_helper.rb
+++ b/app/helpers/wiki_helper.rb
@@ -44,7 +44,10 @@ def wiki_breadcrumb_collapsed_links(page_slug)
     page_slug_split
       .map do |dir_or_page|
         current_slug = "#{current_slug}#{dir_or_page}/"
-        add_to_breadcrumb_collapsed_links link_to(WikiPage.unhyphenize(dir_or_page).capitalize, wiki_page_path(@wiki, current_slug)), location: :after
+        add_to_breadcrumb_collapsed_links(
+          { text: WikiPage.unhyphenize(dir_or_page).capitalize, href: wiki_page_path(@wiki, current_slug) },
+          location: :after
+        )
       end
   end
 
diff --git a/app/views/layouts/nav/breadcrumbs/_breadcrumbs.html.haml b/app/views/layouts/nav/breadcrumbs/_breadcrumbs.html.haml
index 11b765c3ceb5c2cf3ce7dc26588d8479385e6742..33a22dc870fcd0d6de6a75148f2ad620d2abf16b 100644
--- a/app/views/layouts/nav/breadcrumbs/_breadcrumbs.html.haml
+++ b/app/views/layouts/nav/breadcrumbs/_breadcrumbs.html.haml
@@ -4,22 +4,25 @@
 - unless @skip_current_level_breadcrumb
   - push_to_schema_breadcrumb(@breadcrumb_title, breadcrumb_title_link)
 
-%nav.breadcrumbs.gl-breadcrumbs.tmp-breadcrumbs-fix{ 'aria-label': _('Breadcrumbs'), data: { testid: 'breadcrumb-links' } }
-  %ul.breadcrumb.gl-breadcrumb-list.js-breadcrumbs-list.gl-flex-grow-1
-    - unless hide_top_links
-      = header_title
-    - if @breadcrumbs_extra_links
-      - @breadcrumbs_extra_links.each do |extra|
-        = breadcrumb_list_item link_to(extra[:text], extra[:link])
-    = render "layouts/nav/breadcrumbs/collapsed_inline_list", location: :after
-    - unless @skip_current_level_breadcrumb
-      %li.gl-breadcrumb-item{ data: { testid: 'breadcrumb-current-link' } }
-        = link_to(@breadcrumb_title, breadcrumb_title_link)
-  -# haml-lint:disable InlineJavaScript
-  %script{ type: 'application/ld+json' }
-    :plain
-      #{schema_breadcrumb_json}
-= yield :header_content
+-# See https://schema.org/BreadcrumbList
+-# haml-lint:disable InlineJavaScript
+%script{ type: 'application/ld+json' }
+  :plain
+    #{schema_breadcrumb_json}
 
 - if Feature.enabled?(:vue_page_breadcrumbs)
   #js-vue-page-breadcrumbs{ data: { breadcrumbs_json: breadcrumbs_as_json } }
+- else
+  %nav.breadcrumbs.gl-breadcrumbs.tmp-breadcrumbs-fix{ 'aria-label': _('Breadcrumbs'), data: { testid: 'breadcrumb-links' } }
+    %ul.breadcrumb.gl-breadcrumb-list.js-breadcrumbs-list.gl-flex-grow-1
+      - unless hide_top_links
+        = header_title
+      - if @breadcrumbs_extra_links
+        - @breadcrumbs_extra_links.each do |extra|
+          = breadcrumb_list_item link_to(extra[:text], extra[:link])
+      = render "layouts/nav/breadcrumbs/collapsed_inline_list", location: :after
+      - unless @skip_current_level_breadcrumb
+        %li.gl-breadcrumb-item{ data: { testid: 'breadcrumb-current-link' } }
+          = link_to(@breadcrumb_title, breadcrumb_title_link)
+
+= yield :header_content
diff --git a/app/views/layouts/nav/breadcrumbs/_collapsed_inline_list.html.haml b/app/views/layouts/nav/breadcrumbs/_collapsed_inline_list.html.haml
index 37bf8515a8c1558849d12094cf445f5519c240b6..c779bc98c7e3d7bb3426397f6a503de979d272e6 100644
--- a/app/views/layouts/nav/breadcrumbs/_collapsed_inline_list.html.haml
+++ b/app/views/layouts/nav/breadcrumbs/_collapsed_inline_list.html.haml
@@ -4,6 +4,9 @@
   %li.expander.gl-breadcrumb-item.gl-display-inline-flex
     %button.text-expander.has-tooltip.js-breadcrumbs-collapsed-expander.gl-ml-0{ type: "button", data: { container: "body" }, "aria-label": button_tooltip, title: button_tooltip }
       = sprite_icon("ellipsis_h", size: 12)
-  - @breadcrumb_collapsed_links[dropdown_location].each_with_index do |link, index|
+  - @breadcrumb_collapsed_links[dropdown_location].each_with_index do |item, index|
     %li.gl-breadcrumb-item{ :class => "gl-display-none!" }
-      = link
+      = link_to item[:href] do
+        - if item[:avatar_url]
+          = render Pajamas::AvatarComponent.new(item[:avatar_url], alt: item[:text], class: "avatar-tile", size: 16)
+        = item[:text]
diff --git a/spec/components/pajamas/avatar_component_spec.rb b/spec/components/pajamas/avatar_component_spec.rb
index 9c1a40ad5b5c7928a2491740cfb283bfecc35dcd..f148f62d1df76cbc6548c293e874148bb550a44c 100644
--- a/spec/components/pajamas/avatar_component_spec.rb
+++ b/spec/components/pajamas/avatar_component_spec.rb
@@ -50,6 +50,15 @@
   end
 
   describe "avatar image" do
+    context "when src is a string" do
+      let(:item) { "https://uploads.example.com/avatars/123.png" }
+
+      it "uses that string as image src" do
+        render_inline(described_class.new(item))
+        expect(page).to have_css "img.gl-avatar[src='#{item}']"
+      end
+    end
+
     context "when it has an uploaded image" do
       let(:item) { project }
 
@@ -101,7 +110,7 @@
       let(:item) { user }
 
       it "uses a gravatar" do
-        expect(rendered_content).to match /gravatar\.com/
+        expect(rendered_content).to match(/gravatar\.com/)
       end
     end
 
@@ -110,7 +119,7 @@
         let(:item) { Pajamas::AvatarEmail.new('') }
 
         it "uses the default avatar" do
-          expect(rendered_content).to match /no_avatar/
+          expect(rendered_content).to match(/no_avatar/)
         end
       end
 
@@ -118,7 +127,7 @@
         let(:item) { email }
 
         it "uses a agravatar" do
-          expect(rendered_content).to match /gravatar\.com/
+          expect(rendered_content).to match(/gravatar\.com/)
         end
       end
     end
diff --git a/spec/components/previews/pajamas/avatar_component_preview.rb b/spec/components/previews/pajamas/avatar_component_preview.rb
index 147d89169b08046e86cdc31d8a48dd2daceef086..73ae0758aa53c50740eb0579fc3018fd398b7553 100644
--- a/spec/components/previews/pajamas/avatar_component_preview.rb
+++ b/spec/components/previews/pajamas/avatar_component_preview.rb
@@ -3,9 +3,17 @@ module Pajamas
   class AvatarComponentPreview < ViewComponent::Preview
     # Avatar
     # ----
-    # See its design reference [here](https://design.gitlab.com/components/avatar).
-    def default
-      user
+    # See its Pajamas design reference [here](https://design.gitlab.com/components/avatar).
+    #
+    # The avatar component takes a single `item` param and a couple of optional arguments:
+    # - If the `item` is a plain `String`, this string will become the image `src`. In this case, also provide the
+    #   `alt:` option, otherwise the resulting avatar image won't have an alt attribute.
+    # - If the `item` is a `User` object, the avatar will have a round shape.
+    # - For any other object (`Group`, `Project` etc) the shape will be rectangular with rounded corners.
+    # @param src text
+    # @param size select {{ Pajamas::AvatarComponent::SIZE_OPTIONS }}
+    def default(src: ActionController::Base.helpers.image_path('logo.svg'), size: 64)
+      render(Pajamas::AvatarComponent.new(src, size: size))
     end
 
     # We show user avatars in a circle.
diff --git a/spec/features/breadcrumbs_schema_markup_spec.rb b/spec/features/breadcrumbs_schema_markup_spec.rb
index 6610519cd24dd23fa078d88c71d2f638c827c5f1..ca4a8e8ab864b9a3474b3a4827ef17667056ec23 100644
--- a/spec/features/breadcrumbs_schema_markup_spec.rb
+++ b/spec/features/breadcrumbs_schema_markup_spec.rb
@@ -9,6 +9,8 @@
   let_it_be(:group) { create(:group, :public) }
   let_it_be(:subgroup) { create(:group, :public, parent: group) }
   let_it_be(:group_project) { create(:project, :public, namespace: subgroup) }
+  let_it_be(:wiki_home_page) { create(:wiki_page, project: project, title: 'home') }
+  let_it_be(:wiki_sub_page) { create(:wiki_page, project: project, title: 'home/subpage') }
 
   it 'generates the breadcrumb schema for user projects' do
     visit project_url(project)
@@ -87,6 +89,28 @@
     expect(item_list[3]['item']).to eq project_issue_url(project, issue)
   end
 
+  it 'generates the breadcrumb schema for wiki pages' do
+    visit project_wiki_path(project, wiki_sub_page)
+
+    item_list = get_schema_content
+
+    expect(item_list.size).to eq 5
+    expect(item_list[0]['name']).to eq project.namespace.name
+    expect(item_list[0]['item']).to eq user_url(project.first_owner)
+
+    expect(item_list[1]['name']).to eq project.name
+    expect(item_list[1]['item']).to eq project_url(project)
+
+    expect(item_list[2]['name']).to eq 'Wiki'
+    expect(item_list[2]['item']).to eq project_wiki_url(project, wiki_home_page)
+
+    expect(item_list[3]['name']).to eq 'Home'
+    expect(item_list[3]['item']).to eq "#{project_wiki_url(project, wiki_home_page)}/"
+
+    expect(item_list[4]['name']).to eq 'subpage'
+    expect(item_list[4]['item']).to eq project_wiki_url(project, wiki_sub_page)
+  end
+
   def get_schema_content
     content = find('script[type="application/ld+json"]', visible: false).text(:all)
 
diff --git a/spec/helpers/breadcrumbs_helper_spec.rb b/spec/helpers/breadcrumbs_helper_spec.rb
index e5968d22d63f3db8fb414486a794ab4df7828d20..73f1898c222325af3e28d7f2a63811a33db82352 100644
--- a/spec/helpers/breadcrumbs_helper_spec.rb
+++ b/spec/helpers/breadcrumbs_helper_spec.rb
@@ -17,43 +17,12 @@
 
     subject { helper.push_to_schema_breadcrumb(element_name, link) }
 
-    it 'enqueue element name, link and position' do
+    it 'enqueues element name, link' do
       subject
 
       aggregate_failures do
-        expect(breadcrumb_list[0]['name']).to eq element_name
-        expect(breadcrumb_list[0]['item']).to eq link
-        expect(breadcrumb_list[0]['position']).to eq(1)
-      end
-    end
-
-    context 'when link is relative' do
-      let(:link) { '/foo' }
-
-      it 'converts the url into absolute' do
-        subject
-
-        expect(breadcrumb_list[0]['item']).to eq "http://test.host#{link}"
-      end
-    end
-
-    describe 'when link is invalid' do
-      let(:link) { 'invalid://foo[]' }
-
-      it 'returns the current url' do
-        subject
-
-        expect(breadcrumb_list[0]['item']).to eq 'http://test.host'
-      end
-    end
-
-    describe 'when link is nil' do
-      let(:link) { nil }
-
-      it 'returns the current url' do
-        subject
-
-        expect(breadcrumb_list[0]['item']).to eq 'http://test.host'
+        expect(breadcrumb_list[0][:text]).to eq element_name
+        expect(breadcrumb_list[0][:href]).to eq link
       end
     end
   end
@@ -86,6 +55,33 @@
       expect(subject).to eq expected_result
     end
 
+    context 'when link is relative' do
+      let(:link) { '/foo' }
+
+      it 'converts the url into absolute' do
+        add_to_breadcrumbs('no base', link)
+        expect(Gitlab::Json.parse(subject)['itemListElement'][0]['item']).to eq "http://test.host#{link}"
+      end
+    end
+
+    describe 'when link is invalid' do
+      let(:link) { 'invalid://foo[]' }
+
+      it 'returns the current url' do
+        add_to_breadcrumbs('invalid', link)
+        expect(Gitlab::Json.parse(subject)['itemListElement'][0]['item']).to eq 'http://test.host'
+      end
+    end
+
+    describe 'when link is nil' do
+      let(:link) { nil }
+
+      it 'returns the current url' do
+        add_to_breadcrumbs('nil', link)
+        expect(Gitlab::Json.parse(subject)['itemListElement'][0]['item']).to eq 'http://test.host'
+      end
+    end
+
     context 'when extra breadcrumb element is added' do
       let(:extra_elements) do
         [
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 1e47810846810be8544996ec1db69737cf8371c4..6c4c7fc2d1fd76507da804b5da2142069dadfb6f 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -339,6 +339,9 @@
 
       # Experimental merge request dashboard
       stub_feature_flags(merge_request_dashboard: false)
+
+      # Disable new Vue breadcrumbs while feature flag is still in wip state
+      stub_feature_flags(vue_page_breadcrumbs: false)
     else
       unstub_all_feature_flags
     end
diff --git a/spec/views/layouts/nav/breadcrumbs/_breadcrumbs.html.haml_spec.rb b/spec/views/layouts/nav/breadcrumbs/_breadcrumbs.html.haml_spec.rb
index 1436cde877bdc1ccbc6a627d1e284ec808b8dba9..283f971c139c6eeaa59a917a735124edb529586d 100644
--- a/spec/views/layouts/nav/breadcrumbs/_breadcrumbs.html.haml_spec.rb
+++ b/spec/views/layouts/nav/breadcrumbs/_breadcrumbs.html.haml_spec.rb
@@ -4,6 +4,10 @@
 
 RSpec.describe 'layouts/nav/breadcrumbs/_breadcrumbs', feature_category: :navigation do
   describe 'element for Vue page breadcrumbs' do
+    before do
+      stub_feature_flags(vue_page_breadcrumbs: true)
+    end
+
     context 'when the page has breadcrumbs' do
       let(:expected_json) do
         [