diff --git a/packages/react-devtools-shared/src/__tests__/__snapshots__/treeContext-test.js.snap b/packages/react-devtools-shared/src/__tests__/__snapshots__/treeContext-test.js.snap
index 3130bd29239f9..1e72710e4bd2b 100644
--- a/packages/react-devtools-shared/src/__tests__/__snapshots__/treeContext-test.js.snap
+++ b/packages/react-devtools-shared/src/__tests__/__snapshots__/treeContext-test.js.snap
@@ -15,6 +15,7 @@ Object {
   "numElements": 5,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -73,6 +74,7 @@ Object {
     },
   ],
   "ownerID": 4,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -131,6 +133,7 @@ Object {
     },
   ],
   "ownerID": 4,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -145,6 +148,7 @@ Object {
   "numElements": 5,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -165,6 +169,7 @@ Object {
   "numElements": 2,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -193,6 +198,7 @@ Object {
     },
   ],
   "ownerID": 3,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -207,6 +213,7 @@ Object {
   "numElements": 1,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -235,6 +242,7 @@ Object {
     },
   ],
   "ownerID": 2,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -249,6 +257,7 @@ Object {
   "numElements": 0,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -271,6 +280,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -328,6 +338,7 @@ Object {
     },
   ],
   "ownerID": 3,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -371,6 +382,7 @@ Object {
     },
   ],
   "ownerID": 3,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -399,6 +411,7 @@ Object {
     },
   ],
   "ownerID": 3,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -421,6 +434,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -478,6 +492,7 @@ Object {
     },
   ],
   "ownerID": 3,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -492,6 +507,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -512,6 +528,7 @@ Object {
   "numElements": 2,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -526,6 +543,7 @@ Object {
   "numElements": 2,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 0,
   "searchResults": Array [
     3,
@@ -542,6 +560,7 @@ Object {
   "numElements": 3,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 0,
   "searchResults": Array [
     3,
@@ -567,6 +586,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -581,6 +601,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 0,
   "searchResults": Array [
     3,
@@ -598,6 +619,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 0,
   "searchResults": Array [
     2,
@@ -614,6 +636,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "y",
@@ -628,6 +651,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 0,
   "searchResults": Array [
     5,
@@ -651,6 +675,7 @@ Object {
   "numElements": 3,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -665,6 +690,7 @@ Object {
   "numElements": 3,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 0,
   "searchResults": Array [
     3,
@@ -682,6 +708,7 @@ Object {
   "numElements": 3,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 1,
   "searchResults": Array [
     3,
@@ -699,6 +726,7 @@ Object {
   "numElements": 2,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 0,
   "searchResults": Array [
     3,
@@ -723,6 +751,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -737,6 +766,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 0,
   "searchResults": Array [
     3,
@@ -755,6 +785,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 1,
   "searchResults": Array [
     3,
@@ -773,6 +804,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 2,
   "searchResults": Array [
     3,
@@ -791,6 +823,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 1,
   "searchResults": Array [
     3,
@@ -809,6 +842,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 0,
   "searchResults": Array [
     3,
@@ -827,6 +861,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 2,
   "searchResults": Array [
     3,
@@ -845,6 +880,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": 0,
   "searchResults": Array [
     3,
@@ -871,6 +907,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -885,6 +922,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -899,6 +937,7 @@ Object {
   "numElements": 2,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -913,6 +952,7 @@ Object {
   "numElements": 0,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -921,6 +961,37 @@ Object {
 }
 `;
 
+exports[`TreeListContext tree state should navigate next/previous sibling and skip over children in between: 0: mount 1`] = `
+[root]
+  ▾ <Grandparent>
+    ▾ <Parent>
+        <Child key="0">
+    ▾ <Parent>
+        <Child key="0">
+        <Child key="1">
+        <Child key="2">
+    ▾ <Parent>
+        <Child key="0">
+        <Child key="1">
+`;
+
+exports[`TreeListContext tree state should navigate the owner hierarchy: 0: mount 1`] = `
+[root]
+  ▾ <Grandparent>
+    ▾ <Wrapper>
+      ▾ <Parent>
+          <Child key="0">
+    ▾ <Wrapper>
+      ▾ <Parent>
+          <Child key="0">
+          <Child key="1">
+          <Child key="2">
+    ▾ <Wrapper>
+      ▾ <Parent>
+          <Child key="0">
+          <Child key="1">
+`;
+
 exports[`TreeListContext tree state should select child elements: 0: mount 1`] = `
 [root]
   ▾ <Grandparent>
@@ -938,6 +1009,7 @@ Object {
   "numElements": 7,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -952,6 +1024,7 @@ Object {
   "numElements": 7,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -966,6 +1039,7 @@ Object {
   "numElements": 7,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -980,6 +1054,7 @@ Object {
   "numElements": 7,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1005,6 +1080,7 @@ Object {
   "numElements": 7,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1019,6 +1095,7 @@ Object {
   "numElements": 7,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1033,6 +1110,7 @@ Object {
   "numElements": 7,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1047,6 +1125,7 @@ Object {
   "numElements": 7,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1069,6 +1148,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1083,6 +1163,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1097,6 +1178,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1111,6 +1193,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1125,6 +1208,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1139,6 +1223,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1153,6 +1238,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1167,6 +1253,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1181,6 +1268,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
@@ -1195,6 +1283,7 @@ Object {
   "numElements": 4,
   "ownerFlatTree": null,
   "ownerID": null,
+  "ownerSubtreeLeafElementID": null,
   "searchIndex": null,
   "searchResults": Array [],
   "searchText": "",
diff --git a/packages/react-devtools-shared/src/__tests__/treeContext-test.js b/packages/react-devtools-shared/src/__tests__/treeContext-test.js
index b42fb0a9b6f7a..0b672f91415f6 100644
--- a/packages/react-devtools-shared/src/__tests__/treeContext-test.js
+++ b/packages/react-devtools-shared/src/__tests__/treeContext-test.js
@@ -268,6 +268,208 @@ describe('TreeListContext', () => {
 
       done();
     });
+
+    it('should navigate next/previous sibling and skip over children in between', () => {
+      const Grandparent = () => (
+        <React.Fragment>
+          <Parent numChildren={1} />
+          <Parent numChildren={3} />
+          <Parent numChildren={2} />
+        </React.Fragment>
+      );
+      const Parent = ({numChildren}) =>
+        new Array(numChildren)
+          .fill(true)
+          .map((_, index) => <Child key={index} />);
+      const Child = () => null;
+
+      utils.act(() =>
+        ReactDOM.render(<Grandparent />, document.createElement('div')),
+      );
+
+      /*
+       * 0  ▾ <Grandparent>
+       * 1    ▾ <Parent>
+       * 2        <Child key="0">
+       * 3    ▾ <Parent>
+       * 4        <Child key="0">
+       * 5        <Child key="1">
+       * 6        <Child key="2">
+       * 7    ▾ <Parent>
+       * 8        <Child key="0">
+       * 9        <Child key="1">
+       */
+
+      expect(store).toMatchSnapshot('0: mount');
+
+      let renderer;
+      utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
+
+      const firstParentID = ((store.getElementIDAtIndex(1): any): number);
+
+      utils.act(() =>
+        dispatch({type: 'SELECT_ELEMENT_BY_ID', payload: firstParentID}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(1);
+
+      utils.act(() => dispatch({type: 'SELECT_NEXT_SIBLING_IN_TREE'}));
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(3);
+
+      utils.act(() => dispatch({type: 'SELECT_NEXT_SIBLING_IN_TREE'}));
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(7);
+
+      utils.act(() => dispatch({type: 'SELECT_NEXT_SIBLING_IN_TREE'}));
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(1);
+
+      utils.act(() => dispatch({type: 'SELECT_PREVIOUS_SIBLING_IN_TREE'}));
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(7);
+
+      utils.act(() => dispatch({type: 'SELECT_PREVIOUS_SIBLING_IN_TREE'}));
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(3);
+
+      utils.act(() => dispatch({type: 'SELECT_PREVIOUS_SIBLING_IN_TREE'}));
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(1);
+    });
+
+    it('should navigate the owner hierarchy', () => {
+      const Wrapper = ({children}) => children;
+      const Grandparent = () => (
+        <React.Fragment>
+          <Wrapper>
+            <Parent numChildren={1} />
+          </Wrapper>
+          <Wrapper>
+            <Parent numChildren={3} />
+          </Wrapper>
+          <Wrapper>
+            <Parent numChildren={2} />
+          </Wrapper>
+        </React.Fragment>
+      );
+      const Parent = ({numChildren}) =>
+        new Array(numChildren)
+          .fill(true)
+          .map((_, index) => <Child key={index} />);
+      const Child = () => null;
+
+      utils.act(() =>
+        ReactDOM.render(<Grandparent />, document.createElement('div')),
+      );
+
+      /*
+       *  0  ▾ <Grandparent>
+       *  1    ▾ <Wrapper>
+       *  2      ▾ <Parent>
+       *  3          <Child key="0">
+       *  4    ▾ <Wrapper>
+       *  5      ▾ <Parent>
+       *  6          <Child key="0">
+       *  7          <Child key="1">
+       *  8          <Child key="2">
+       *  9    ▾ <Wrapper>
+       * 10      ▾ <Parent>
+       * 11          <Child key="0">
+       * 12          <Child key="1">
+       */
+
+      expect(store).toMatchSnapshot('0: mount');
+
+      let renderer;
+      utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
+
+      const childID = ((store.getElementIDAtIndex(7): any): number);
+      utils.act(() =>
+        dispatch({type: 'SELECT_ELEMENT_BY_ID', payload: childID}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.ownerSubtreeLeafElementID).toBeNull();
+      expect(state.selectedElementIndex).toBe(7);
+
+      // Basic navigation test
+      utils.act(() =>
+        dispatch({type: 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE'}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.ownerSubtreeLeafElementID).toBe(childID);
+      expect(state.selectedElementIndex).toBe(5);
+
+      utils.act(() =>
+        dispatch({type: 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE'}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(0);
+
+      utils.act(() =>
+        dispatch({type: 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE'}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(0); // noop since we're at the top
+
+      utils.act(() =>
+        dispatch({type: 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE'}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(5);
+
+      utils.act(() =>
+        dispatch({type: 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE'}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(7);
+
+      utils.act(() =>
+        dispatch({type: 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE'}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(7); // noop since we're at the leaf node
+
+      // Other navigational actions should clear out the temporary owner chain.
+      utils.act(() => dispatch({type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE'}));
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(6);
+      expect(state.ownerSubtreeLeafElementID).toBeNull();
+
+      const parentID = ((store.getElementIDAtIndex(5): any): number);
+      utils.act(() =>
+        dispatch({type: 'SELECT_ELEMENT_BY_ID', payload: parentID}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.ownerSubtreeLeafElementID).toBeNull();
+      expect(state.selectedElementIndex).toBe(5);
+
+      // It should not be possible to navigate beyond the owner chain leaf.
+      utils.act(() =>
+        dispatch({type: 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE'}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.ownerSubtreeLeafElementID).toBe(parentID);
+      expect(state.selectedElementIndex).toBe(0);
+
+      utils.act(() =>
+        dispatch({type: 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE'}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(0); // noop since we're at the top
+
+      utils.act(() =>
+        dispatch({type: 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE'}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(5);
+
+      utils.act(() =>
+        dispatch({type: 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE'}),
+      );
+      utils.act(() => renderer.update(<Contexts />));
+      expect(state.selectedElementIndex).toBe(5); // noop since we're at the leaf node
+    });
   });
 
   describe('search state', () => {
diff --git a/packages/react-devtools-shared/src/devtools/views/Components/Tree.js b/packages/react-devtools-shared/src/devtools/views/Components/Tree.js
index 8b9f66f39f844..fe5221a66f166 100644
--- a/packages/react-devtools-shared/src/devtools/views/Components/Tree.js
+++ b/packages/react-devtools-shared/src/devtools/views/Components/Tree.js
@@ -130,7 +130,11 @@ export default function Tree(props: Props) {
       switch (event.key) {
         case 'ArrowDown':
           event.preventDefault();
-          dispatch({type: 'SELECT_NEXT_ELEMENT_IN_TREE'});
+          if (event.altKey) {
+            dispatch({type: 'SELECT_NEXT_SIBLING_IN_TREE'});
+          } else {
+            dispatch({type: 'SELECT_NEXT_ELEMENT_IN_TREE'});
+          }
           break;
         case 'ArrowLeft':
           event.preventDefault();
@@ -139,10 +143,16 @@ export default function Tree(props: Props) {
               ? store.getElementByID(selectedElementID)
               : null;
           if (element !== null) {
-            if (element.children.length > 0 && !element.isCollapsed) {
-              store.toggleIsCollapsed(element.id, true);
+            if (event.altKey) {
+              if (element.ownerID !== null) {
+                dispatch({type: 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE'});
+              }
             } else {
-              dispatch({type: 'SELECT_PARENT_ELEMENT_IN_TREE'});
+              if (element.children.length > 0 && !element.isCollapsed) {
+                store.toggleIsCollapsed(element.id, true);
+              } else {
+                dispatch({type: 'SELECT_PARENT_ELEMENT_IN_TREE'});
+              }
             }
           }
           break;
@@ -153,16 +163,24 @@ export default function Tree(props: Props) {
               ? store.getElementByID(selectedElementID)
               : null;
           if (element !== null) {
-            if (element.children.length > 0 && element.isCollapsed) {
-              store.toggleIsCollapsed(element.id, false);
+            if (event.altKey) {
+              dispatch({type: 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE'});
             } else {
-              dispatch({type: 'SELECT_CHILD_ELEMENT_IN_TREE'});
+              if (element.children.length > 0 && element.isCollapsed) {
+                store.toggleIsCollapsed(element.id, false);
+              } else {
+                dispatch({type: 'SELECT_CHILD_ELEMENT_IN_TREE'});
+              }
             }
           }
           break;
         case 'ArrowUp':
           event.preventDefault();
-          dispatch({type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE'});
+          if (event.altKey) {
+            dispatch({type: 'SELECT_PREVIOUS_SIBLING_IN_TREE'});
+          } else {
+            dispatch({type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE'});
+          }
           break;
         default:
           return;
diff --git a/packages/react-devtools-shared/src/devtools/views/Components/TreeContext.js b/packages/react-devtools-shared/src/devtools/views/Components/TreeContext.js
index a2aac21026459..f748a9f3548af 100644
--- a/packages/react-devtools-shared/src/devtools/views/Components/TreeContext.js
+++ b/packages/react-devtools-shared/src/devtools/views/Components/TreeContext.js
@@ -49,6 +49,7 @@ import type {Element} from './types';
 export type StateContext = {|
   // Tree
   numElements: number,
+  ownerSubtreeLeafElementID: number | null,
   selectedElementID: number | null,
   selectedElementIndex: number | null,
 
@@ -92,15 +93,27 @@ type ACTION_SELECT_ELEMENT_BY_ID = {|
 type ACTION_SELECT_NEXT_ELEMENT_IN_TREE = {|
   type: 'SELECT_NEXT_ELEMENT_IN_TREE',
 |};
+type ACTION_SELECT_NEXT_SIBLING_IN_TREE = {|
+  type: 'SELECT_NEXT_SIBLING_IN_TREE',
+|};
+type ACTION_SELECT_OWNER = {|
+  type: 'SELECT_OWNER',
+  payload: number,
+|};
 type ACTION_SELECT_PARENT_ELEMENT_IN_TREE = {|
   type: 'SELECT_PARENT_ELEMENT_IN_TREE',
 |};
 type ACTION_SELECT_PREVIOUS_ELEMENT_IN_TREE = {|
   type: 'SELECT_PREVIOUS_ELEMENT_IN_TREE',
 |};
-type ACTION_SELECT_OWNER = {|
-  type: 'SELECT_OWNER',
-  payload: number,
+type ACTION_SELECT_PREVIOUS_SIBLING_IN_TREE = {|
+  type: 'SELECT_PREVIOUS_SIBLING_IN_TREE',
+|};
+type ACTION_SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE = {|
+  type: 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE',
+|};
+type ACTION_SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE = {|
+  type: 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE',
 |};
 type ACTION_SET_SEARCH_TEXT = {|
   type: 'SET_SEARCH_TEXT',
@@ -119,9 +132,13 @@ type Action =
   | ACTION_SELECT_ELEMENT_AT_INDEX
   | ACTION_SELECT_ELEMENT_BY_ID
   | ACTION_SELECT_NEXT_ELEMENT_IN_TREE
+  | ACTION_SELECT_NEXT_SIBLING_IN_TREE
+  | ACTION_SELECT_OWNER
   | ACTION_SELECT_PARENT_ELEMENT_IN_TREE
   | ACTION_SELECT_PREVIOUS_ELEMENT_IN_TREE
-  | ACTION_SELECT_OWNER
+  | ACTION_SELECT_PREVIOUS_SIBLING_IN_TREE
+  | ACTION_SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE
+  | ACTION_SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE
   | ACTION_SET_SEARCH_TEXT
   | ACTION_UPDATE_INSPECTED_ELEMENT_ID;
 
@@ -140,6 +157,7 @@ TreeDispatcherContext.displayName = 'TreeDispatcherContext';
 type State = {|
   // Tree
   numElements: number,
+  ownerSubtreeLeafElementID: number | null,
   selectedElementID: number | null,
   selectedElementIndex: number | null,
 
@@ -157,7 +175,12 @@ type State = {|
 |};
 
 function reduceTreeState(store: Store, state: State, action: Action): State {
-  let {numElements, selectedElementIndex, selectedElementID} = state;
+  let {
+    numElements,
+    ownerSubtreeLeafElementID,
+    selectedElementIndex,
+    selectedElementID,
+  } = state;
   const ownerID = state.ownerID;
 
   let lookupIDForIndex = true;
@@ -187,6 +210,8 @@ function reduceTreeState(store: Store, state: State, action: Action): State {
         }
         break;
       case 'SELECT_CHILD_ELEMENT_IN_TREE':
+        ownerSubtreeLeafElementID = null;
+
         if (selectedElementIndex !== null) {
           const selectedElement = store.getElementAtIndex(
             ((selectedElementIndex: any): number),
@@ -205,9 +230,13 @@ function reduceTreeState(store: Store, state: State, action: Action): State {
         }
         break;
       case 'SELECT_ELEMENT_AT_INDEX':
+        ownerSubtreeLeafElementID = null;
+
         selectedElementIndex = (action: ACTION_SELECT_ELEMENT_AT_INDEX).payload;
         break;
       case 'SELECT_ELEMENT_BY_ID':
+        ownerSubtreeLeafElementID = null;
+
         // Skip lookup in this case; it would be redundant.
         // It might also cause problems if the specified element was inside of a (not yet expanded) subtree.
         lookupIDForIndex = false;
@@ -219,6 +248,8 @@ function reduceTreeState(store: Store, state: State, action: Action): State {
             : store.getIndexOfElementID(selectedElementID);
         break;
       case 'SELECT_NEXT_ELEMENT_IN_TREE':
+        ownerSubtreeLeafElementID = null;
+
         if (
           selectedElementIndex === null ||
           selectedElementIndex + 1 >= numElements
@@ -228,12 +259,80 @@ function reduceTreeState(store: Store, state: State, action: Action): State {
           selectedElementIndex++;
         }
         break;
+      case 'SELECT_NEXT_SIBLING_IN_TREE':
+        ownerSubtreeLeafElementID = null;
+
+        if (selectedElementIndex !== null) {
+          const selectedElement = store.getElementAtIndex(
+            ((selectedElementIndex: any): number),
+          );
+          if (selectedElement !== null && selectedElement.parentID !== 0) {
+            const parent = store.getElementByID(selectedElement.parentID);
+            if (parent !== null) {
+              const {children} = parent;
+              const selectedChildIndex = children.indexOf(selectedElement.id);
+              const nextChildID =
+                selectedChildIndex < children.length - 1
+                  ? children[selectedChildIndex + 1]
+                  : children[0];
+              selectedElementIndex = store.getIndexOfElementID(nextChildID);
+            }
+          }
+        }
+        break;
+      case 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE':
+        if (selectedElementIndex !== null) {
+          if (
+            ownerSubtreeLeafElementID !== null &&
+            ownerSubtreeLeafElementID !== selectedElementID
+          ) {
+            const leafElement = store.getElementByID(ownerSubtreeLeafElementID);
+            if (leafElement !== null) {
+              let currentElement = leafElement;
+              while (currentElement !== null) {
+                if (currentElement.ownerID === selectedElementID) {
+                  selectedElementIndex = store.getIndexOfElementID(
+                    currentElement.id,
+                  );
+                  break;
+                } else if (currentElement.ownerID !== 0) {
+                  currentElement = store.getElementByID(currentElement.ownerID);
+                }
+              }
+            }
+          }
+        }
+        break;
+      case 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE':
+        if (selectedElementIndex !== null) {
+          if (ownerSubtreeLeafElementID === null) {
+            // If this is the first time we're stepping through the owners tree,
+            // pin the current component as the owners list leaf.
+            // This will enable us to step back down to this component.
+            ownerSubtreeLeafElementID = selectedElementID;
+          }
+
+          const selectedElement = store.getElementAtIndex(
+            ((selectedElementIndex: any): number),
+          );
+          if (selectedElement !== null && selectedElement.ownerID !== 0) {
+            const ownerIndex = store.getIndexOfElementID(
+              selectedElement.ownerID,
+            );
+            if (ownerIndex !== null) {
+              selectedElementIndex = ownerIndex;
+            }
+          }
+        }
+        break;
       case 'SELECT_PARENT_ELEMENT_IN_TREE':
+        ownerSubtreeLeafElementID = null;
+
         if (selectedElementIndex !== null) {
           const selectedElement = store.getElementAtIndex(
             ((selectedElementIndex: any): number),
           );
-          if (selectedElement !== null && selectedElement.parentID !== null) {
+          if (selectedElement !== null && selectedElement.parentID !== 0) {
             const parentIndex = store.getIndexOfElementID(
               selectedElement.parentID,
             );
@@ -244,12 +343,35 @@ function reduceTreeState(store: Store, state: State, action: Action): State {
         }
         break;
       case 'SELECT_PREVIOUS_ELEMENT_IN_TREE':
+        ownerSubtreeLeafElementID = null;
+
         if (selectedElementIndex === null || selectedElementIndex === 0) {
           selectedElementIndex = numElements - 1;
         } else {
           selectedElementIndex--;
         }
         break;
+      case 'SELECT_PREVIOUS_SIBLING_IN_TREE':
+        ownerSubtreeLeafElementID = null;
+
+        if (selectedElementIndex !== null) {
+          const selectedElement = store.getElementAtIndex(
+            ((selectedElementIndex: any): number),
+          );
+          if (selectedElement !== null && selectedElement.parentID !== 0) {
+            const parent = store.getElementByID(selectedElement.parentID);
+            if (parent !== null) {
+              const {children} = parent;
+              const selectedChildIndex = children.indexOf(selectedElement.id);
+              const nextChildID =
+                selectedChildIndex > 0
+                  ? children[selectedChildIndex - 1]
+                  : children[children.length - 1];
+              selectedElementIndex = store.getIndexOfElementID(nextChildID);
+            }
+          }
+        }
+        break;
       default:
         // React can bailout of no-op updates.
         return state;
@@ -271,6 +393,7 @@ function reduceTreeState(store: Store, state: State, action: Action): State {
     ...state,
 
     numElements,
+    ownerSubtreeLeafElementID,
     selectedElementIndex,
     selectedElementID,
   };
@@ -653,8 +776,12 @@ function TreeContextController({
         case 'SELECT_ELEMENT_BY_ID':
         case 'SELECT_CHILD_ELEMENT_IN_TREE':
         case 'SELECT_NEXT_ELEMENT_IN_TREE':
+        case 'SELECT_NEXT_SIBLING_IN_TREE':
+        case 'SELECT_OWNER_LIST_NEXT_ELEMENT_IN_TREE':
+        case 'SELECT_OWNER_LIST_PREVIOUS_ELEMENT_IN_TREE':
         case 'SELECT_PARENT_ELEMENT_IN_TREE':
         case 'SELECT_PREVIOUS_ELEMENT_IN_TREE':
+        case 'SELECT_PREVIOUS_SIBLING_IN_TREE':
         case 'SELECT_OWNER':
         case 'UPDATE_INSPECTED_ELEMENT_ID':
         case 'SET_SEARCH_TEXT':
@@ -687,6 +814,7 @@ function TreeContextController({
   const [state, dispatch] = useReducer(reducer, {
     // Tree
     numElements: store.numElements,
+    ownerSubtreeLeafElementID: null,
     selectedElementID:
       defaultSelectedElementID == null ? null : defaultSelectedElementID,
     selectedElementIndex: