diff --git a/packages/core-api/src/routing/hooks.test.tsx b/packages/core-api/src/routing/hooks.test.tsx
new file mode 100644
index 0000000000..c08891c8cc
--- /dev/null
+++ b/packages/core-api/src/routing/hooks.test.tsx
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { render } from '@testing-library/react';
+import React, { PropsWithChildren } from 'react';
+import { MemoryRouter, Routes } from 'react-router-dom';
+import { createRoutableExtension } from '../extensions';
+import {
+ childDiscoverer,
+ routeElementDiscoverer,
+ traverseElementTree,
+} from '../extensions/traversal';
+import { createPlugin } from '../plugin';
+import { routeCollector, routeParentCollector } from './collectors';
+import { useRouteRef, RoutingProvider } from './hooks';
+import { createRouteRef } from './RouteRef';
+
+const mockConfig = () => ({ path: '/unused', title: 'Unused' });
+const MockComponent = ({ children }: PropsWithChildren<{}>) => <>{children}>;
+
+const plugin = createPlugin({ id: 'my-plugin' });
+
+const ref1 = createRouteRef(mockConfig());
+const ref2 = createRouteRef(mockConfig());
+const ref3 = createRouteRef(mockConfig());
+
+const MockRouteSource = () => {
+ const routeFunc = useRouteRef(ref2);
+ expect(routeFunc?.()).toBe('/foo/bar');
+ return null;
+};
+
+const Extension1 = plugin.provide(
+ createRoutableExtension({ component: MockComponent, mountPoint: ref1 }),
+);
+const Extension2 = plugin.provide(
+ createRoutableExtension({ component: MockRouteSource, mountPoint: ref2 }),
+);
+const Extension3 = plugin.provide(
+ createRoutableExtension({ component: MockComponent, mountPoint: ref3 }),
+);
+
+describe('discovery', () => {
+ it('should handle all react router Route patterns', () => {
+ const root = (
+
+
+
+
+
+
+
+
+
+ );
+
+ const { routes, routeParents } = traverseElementTree({
+ root,
+ discoverers: [childDiscoverer, routeElementDiscoverer],
+ collectors: {
+ routes: routeCollector,
+ routeParents: routeParentCollector,
+ },
+ });
+
+ render(
+
+ {root}
+ ,
+ );
+
+ expect.assertions(2);
+ });
+});
diff --git a/packages/core-api/src/routing/hooks.tsx b/packages/core-api/src/routing/hooks.tsx
new file mode 100644
index 0000000000..8a2aa72c92
--- /dev/null
+++ b/packages/core-api/src/routing/hooks.tsx
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React, { createContext, ReactNode, useContext } from 'react';
+import { RouteRef } from './types';
+
+export type RouteFunc = () => string;
+
+class RouteResolver {
+ constructor(
+ private readonly routes: Map,
+ private readonly routeParents: Map,
+ ) {}
+
+ resolve(routeRef: RouteRef): RouteFunc {
+ let currentRouteRef: RouteRef | undefined = routeRef;
+ let fullPath = '';
+
+ while (currentRouteRef) {
+ const path = this.routes.get(currentRouteRef);
+ if (!path) {
+ throw new Error(`No path for ${currentRouteRef}`);
+ }
+ fullPath = `${path}${fullPath}`;
+ currentRouteRef = this.routeParents.get(currentRouteRef);
+ }
+
+ return () => {
+ return fullPath;
+ };
+ }
+}
+
+const RoutingContext = createContext(undefined);
+
+export function useRouteRef(routeRef: RouteRef): RouteFunc | undefined {
+ const resolver = useContext(RoutingContext);
+ if (!resolver) {
+ throw new Error('No route resolver found in context');
+ }
+
+ return resolver.resolve(routeRef);
+}
+
+type ProviderProps = {
+ routes: Map;
+ routeParents: Map;
+ children: ReactNode;
+};
+
+export const RoutingProvider = ({
+ routes,
+ routeParents,
+ children,
+}: ProviderProps) => {
+ const resolver = new RouteResolver(routes, routeParents);
+ return (
+
+ {children}
+
+ );
+};