diff --git a/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx b/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx
new file mode 100644
index 0000000000..292cd37ede
--- /dev/null
+++ b/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx
@@ -0,0 +1,114 @@
+/*
+ * 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 from 'react';
+import { render } from '@testing-library/react';
+import { EntityContext } from '../../hooks/useEntity';
+import { Entity } from '@backstage/catalog-model';
+import { EntitySwitch } from './EntitySwitch';
+import { isKind } from './conditions';
+
+describe('EntitySwitch', () => {
+ it('should switch child when entity switches', () => {
+ const content = (
+
+
+
+
+
+ );
+
+ const rendered = render(
+
+ {content}
+ ,
+ );
+
+ expect(rendered.queryByText('A')).toBeInTheDocument();
+ expect(rendered.queryByText('B')).not.toBeInTheDocument();
+ expect(rendered.queryByText('C')).not.toBeInTheDocument();
+
+ rendered.rerender(
+
+ {content}
+ ,
+ );
+
+ expect(rendered.queryByText('A')).not.toBeInTheDocument();
+ expect(rendered.queryByText('B')).toBeInTheDocument();
+ expect(rendered.queryByText('C')).not.toBeInTheDocument();
+
+ rendered.rerender(
+
+ {content}
+ ,
+ );
+
+ expect(rendered.queryByText('A')).not.toBeInTheDocument();
+ expect(rendered.queryByText('B')).not.toBeInTheDocument();
+ expect(rendered.queryByText('C')).toBeInTheDocument();
+ });
+
+ it('should switch child when filters switch', () => {
+ const entityContextValue = {
+ entity: { kind: 'component' } as Entity,
+ loading: false,
+ error: undefined,
+ };
+
+ const rendered = render(
+
+
+
+
+
+ ,
+ );
+
+ expect(rendered.queryByText('A')).toBeInTheDocument();
+ expect(rendered.queryByText('B')).not.toBeInTheDocument();
+
+ rendered.rerender(
+
+
+
+
+
+ ,
+ );
+
+ expect(rendered.queryByText('A')).not.toBeInTheDocument();
+ expect(rendered.queryByText('B')).toBeInTheDocument();
+ });
+});
diff --git a/plugins/catalog/src/components/EntitySwitch/EntitySwitch.tsx b/plugins/catalog/src/components/EntitySwitch/EntitySwitch.tsx
new file mode 100644
index 0000000000..f36bc29cee
--- /dev/null
+++ b/plugins/catalog/src/components/EntitySwitch/EntitySwitch.tsx
@@ -0,0 +1,69 @@
+/*
+ * 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 {
+ ReactNode,
+ PropsWithChildren,
+ Children,
+ Fragment,
+ useMemo,
+ isValidElement,
+} from 'react';
+import { useEntity } from '../../hooks/useEntity';
+import { Entity } from '@backstage/catalog-model';
+
+const EntitySwitchCase = (_: {
+ if?: (entity: Entity) => boolean;
+ children: ReactNode;
+}) => null;
+
+type SwitchCase = {
+ if?: (entity: Entity) => boolean;
+ children: JSX.Element;
+};
+
+function createSwitchCasesFromChildren(children: ReactNode): SwitchCase[] {
+ return Children.toArray(children).flatMap(child => {
+ if (!isValidElement(child)) {
+ return [];
+ }
+
+ if (child.type === Fragment) {
+ return createSwitchCasesFromChildren(child.props.children);
+ }
+
+ if (child.type !== EntitySwitchCase) {
+ throw new Error(`Child of EntitySwitch is not an EntitySwitch.Case`);
+ }
+
+ const { if: condition, children } = child.props;
+ return [{ if: condition, children }];
+ });
+}
+
+export const EntitySwitch = ({ children }: PropsWithChildren<{}>) => {
+ const { entity } = useEntity();
+ const switchCases = useMemo(() => createSwitchCasesFromChildren(children), [
+ children,
+ ]);
+
+ const matchingCase = switchCases.find(switchCase =>
+ switchCase.if ? switchCase.if(entity) : true,
+ );
+ return matchingCase?.children ?? null;
+};
+
+EntitySwitch.Case = EntitySwitchCase;
diff --git a/plugins/catalog/src/components/EntitySwitch/conditions.ts b/plugins/catalog/src/components/EntitySwitch/conditions.ts
new file mode 100644
index 0000000000..8e4da19656
--- /dev/null
+++ b/plugins/catalog/src/components/EntitySwitch/conditions.ts
@@ -0,0 +1,39 @@
+/*
+ * 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 { Entity, ComponentEntity } from '@backstage/catalog-model';
+
+function strCmp(a: string | undefined, b: string | undefined): boolean {
+ return Boolean(a && a?.toLowerCase() === b?.toLowerCase());
+}
+
+export function isKind(kind: string) {
+ return (entity: Entity) => strCmp(entity?.kind, kind);
+}
+
+export function isComponentType(type: string) {
+ return (entity: Entity) => {
+ if (!strCmp(entity?.kind, 'component')) {
+ return false;
+ }
+ const componentEntity = entity as ComponentEntity;
+ return strCmp(componentEntity.spec.type, type);
+ };
+}
+
+export function isNamespace(namespace: string) {
+ return (entity: Entity) => strCmp(entity?.metadata?.namespace, namespace);
+}
diff --git a/plugins/catalog/src/components/EntitySwitch/index.ts b/plugins/catalog/src/components/EntitySwitch/index.ts
new file mode 100644
index 0000000000..6549941187
--- /dev/null
+++ b/plugins/catalog/src/components/EntitySwitch/index.ts
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+export { EntitySwitch } from './EntitySwitch';