diff --git a/frontend/src/metabase/nav/components/SearchBar.jsx b/frontend/src/metabase/nav/components/SearchBar.jsx
index 35943215357cf31ac9f0185ec953bcc1c1407fb3..ba723b656a05b4b93d330c65f312b57fdfbe07d7 100644
--- a/frontend/src/metabase/nav/components/SearchBar.jsx
+++ b/frontend/src/metabase/nav/components/SearchBar.jsx
@@ -1,58 +1,18 @@
 /* eslint-disable react/prop-types */
 import React from "react";
 import ReactDOM from "react-dom";
-import { Flex } from "grid-styled";
-import styled from "styled-components";
-import { space } from "styled-system";
 import { t } from "ttag";
 
-import { color, lighten } from "metabase/lib/colors";
-
 import Card from "metabase/components/Card";
 import Icon from "metabase/components/Icon";
 import OnClickOutsideWrapper from "metabase/components/OnClickOutsideWrapper";
-import SearchResult from "metabase/search/components/SearchResult";
 
-import { DefaultSearchColor } from "metabase/nav/constants";
 import MetabaseSettings from "metabase/lib/settings";
 
-const ActiveSearchColor = lighten(color("nav"), 0.1);
-
-import Search from "metabase/entities/search";
-
-const SearchWrapper = styled(Flex)`
-  position: relative;
-  background-color: ${props =>
-    props.active ? ActiveSearchColor : DefaultSearchColor};
-  border-radius: 6px;
-  flex: 1 1 auto;
-  max-width: 50em;
-  align-items: center;
-  color: white;
-  transition: background 300ms ease-in;
-  &:hover {
-    background-color: ${ActiveSearchColor};
-  }
-`;
-
-const SearchInput = styled.input`
-  ${space};
-  background-color: transparent;
-  width: 100%;
-  border: none;
-  color: white;
-  font-size: 1em;
-  font-weight: 700;
-  &:focus {
-    outline: none;
-  }
-  &::placeholder {
-    color: ${color("text-white")};
-  }
-`;
+import { SearchInput, SearchWrapper } from "./SearchBar.styled";
+import { SearchResults } from "./SearchResults";
 
 const ALLOWED_SEARCH_FOCUS_ELEMENTS = new Set(["BODY", "A"]);
-const SEARCH_LIMIT = 50;
 
 export default class SearchBar extends React.Component {
   state = {
@@ -84,7 +44,7 @@ export default class SearchBar extends React.Component {
       this.setState({ searchText: "" });
     }
   }
-  handleKeyUp = (e: KeyboardEvent) => {
+  handleKeyUp = e => {
     const FORWARD_SLASH_KEY = 191;
     if (
       e.keyCode === FORWARD_SLASH_KEY &&
@@ -95,25 +55,6 @@ export default class SearchBar extends React.Component {
     }
   };
 
-  renderResults(results) {
-    if (results.length === 0) {
-      return (
-        <li className="flex flex-column align-center justify-center p4 text-medium text-centered">
-          <div className="my3">
-            <Icon name="search" mb={1} size={24} />
-            <h3 className="text-light">{t`Didn't find anything`}</h3>
-          </div>
-        </li>
-      );
-    } else {
-      return results.map(l => (
-        <li key={`${l.model}:${l.id}`}>
-          <SearchResult result={l} compact={true} />
-        </li>
-      ));
-    }
-  }
-
   render() {
     const { active, searchText } = this.state;
     return (
@@ -152,20 +93,7 @@ export default class SearchBar extends React.Component {
                   style={{ maxHeight: 400 }}
                   py={1}
                 >
-                  <Search.ListLoader
-                    query={{ q: searchText.trim(), limit: SEARCH_LIMIT }}
-                    wrapped
-                    reload
-                    debounced
-                  >
-                    {({ list }) => {
-                      return (
-                        <ol data-testid="search-results-list">
-                          {this.renderResults(list)}
-                        </ol>
-                      );
-                    }}
-                  </Search.ListLoader>
+                  <SearchResults searchText={searchText.trim()} />
                 </Card>
               ) : null}
             </div>
diff --git a/frontend/src/metabase/nav/components/SearchBar.styled.jsx b/frontend/src/metabase/nav/components/SearchBar.styled.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..ce0239fe01390b51ef07ea180e798dbbaf9da9a7
--- /dev/null
+++ b/frontend/src/metabase/nav/components/SearchBar.styled.jsx
@@ -0,0 +1,39 @@
+import styled from "styled-components";
+import { Flex } from "grid-styled";
+import { space } from "styled-system";
+
+import { DefaultSearchColor } from "metabase/nav/constants";
+import { color, lighten } from "metabase/lib/colors";
+
+const ActiveSearchColor = lighten(color("nav"), 0.1);
+
+export const SearchWrapper = styled(Flex)`
+  position: relative;
+  background-color: ${props =>
+    props.active ? ActiveSearchColor : DefaultSearchColor};
+  border-radius: 6px;
+  flex: 1 1 auto;
+  max-width: 50em;
+  align-items: center;
+  color: white;
+  transition: background 300ms ease-in;
+  &:hover {
+    background-color: ${ActiveSearchColor};
+  }
+`;
+
+export const SearchInput = styled.input`
+  ${space};
+  background-color: transparent;
+  width: 100%;
+  border: none;
+  color: white;
+  font-size: 1em;
+  font-weight: 700;
+  &:focus {
+    outline: none;
+  }
+  &::placeholder {
+    color: ${color("text-white")};
+  }
+`;
diff --git a/frontend/src/metabase/nav/components/SearchResults.jsx b/frontend/src/metabase/nav/components/SearchResults.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..5ecca27f29fb943abbd299075b9fc51ec125430d
--- /dev/null
+++ b/frontend/src/metabase/nav/components/SearchResults.jsx
@@ -0,0 +1,46 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { Box } from "grid-styled";
+import { t } from "ttag";
+import Search from "metabase/entities/search";
+import SearchResult from "metabase/search/components/SearchResult";
+import EmptyState from "metabase/components/EmptyState";
+
+const SEARCH_LIMIT = 50;
+
+const propTypes = {
+  searchText: PropTypes.string,
+};
+
+export const SearchResults = ({ searchText }) => {
+  return (
+    <Search.ListLoader
+      query={{ q: searchText, limit: SEARCH_LIMIT }}
+      wrapped
+      reload
+      debounced
+    >
+      {({ list }) => {
+        const hasResults = list.length > 0;
+
+        return (
+          <ul data-testid="search-results-list">
+            {hasResults ? (
+              list.map(item => (
+                <li key={`${item.model}:${item.id}`}>
+                  <SearchResult result={item} compact={true} />
+                </li>
+              ))
+            ) : (
+              <Box mt={4} mb={3}>
+                <EmptyState message={t`Didn't find anything`} icon="search" />
+              </Box>
+            )}
+          </ul>
+        );
+      }}
+    </Search.ListLoader>
+  );
+};
+
+SearchResults.propTypes = propTypes;
diff --git a/frontend/src/metabase/search/components/CollectionBadge.jsx b/frontend/src/metabase/search/components/CollectionBadge.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..71bece7d5a2438d099ede48721c39dfd6e6fee2e
--- /dev/null
+++ b/frontend/src/metabase/search/components/CollectionBadge.jsx
@@ -0,0 +1,29 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+import * as Urls from "metabase/lib/urls";
+
+import {
+  CollectionBadgeRoot,
+  CollectionLink,
+  AuthorityLevelIcon,
+} from "./CollectionBadge.styled";
+
+const propTypes = {
+  collection: PropTypes.shape({
+    name: PropTypes.string,
+  }),
+};
+
+export function CollectionBadge({ collection }) {
+  return (
+    <CollectionBadgeRoot>
+      <CollectionLink to={Urls.collection(collection)}>
+        <AuthorityLevelIcon collection={collection} />
+        {collection.name}
+      </CollectionLink>
+    </CollectionBadgeRoot>
+  );
+}
+
+CollectionBadge.propTypes = propTypes;
diff --git a/frontend/src/metabase/search/components/CollectionBadge.styled.jsx b/frontend/src/metabase/search/components/CollectionBadge.styled.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..c259dee3242d415145ad1c0732e2ee3cd58f73eb
--- /dev/null
+++ b/frontend/src/metabase/search/components/CollectionBadge.styled.jsx
@@ -0,0 +1,26 @@
+import styled from "styled-components";
+import { color } from "metabase/lib/colors";
+import Link from "metabase/components/Link";
+
+import { PLUGIN_COLLECTION_COMPONENTS } from "metabase/plugins";
+
+const { CollectionAuthorityLevelIcon } = PLUGIN_COLLECTION_COMPONENTS;
+
+export const CollectionBadgeRoot = styled.div`
+  display: inline-block;
+`;
+
+export const CollectionLink = styled(Link)`
+  display: flex;
+  align-items: center;
+  text-decoration: dashed;
+  &:hover {
+    color: ${color("brand")};
+  }
+`;
+
+export const AuthorityLevelIcon = styled(CollectionAuthorityLevelIcon).attrs({
+  size: 13,
+})`
+  padding-right: 2px;
+`;
diff --git a/frontend/src/metabase/search/components/InfoText.jsx b/frontend/src/metabase/search/components/InfoText.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..bf750400f00325307b6f6220ff0170fb80872543
--- /dev/null
+++ b/frontend/src/metabase/search/components/InfoText.jsx
@@ -0,0 +1,87 @@
+import React from "react";
+import { t, jt } from "ttag";
+
+import * as Urls from "metabase/lib/urls";
+import { capitalize } from "metabase/lib/formatting";
+
+import Icon from "metabase/components/Icon";
+import Link from "metabase/components/Link";
+
+import Schema from "metabase/entities/schemas";
+import Database from "metabase/entities/databases";
+import Table from "metabase/entities/tables";
+import { PLUGIN_COLLECTIONS } from "metabase/plugins";
+import { CollectionBadge } from "./CollectionBadge";
+
+function formatCollection(collection) {
+  return collection.id && <CollectionBadge collection={collection} />;
+}
+
+function getCollectionInfoText(collection) {
+  if (PLUGIN_COLLECTIONS.isRegularCollection(collection)) {
+    return t`Collection`;
+  }
+  const level = PLUGIN_COLLECTIONS.AUTHORITY_LEVEL[collection.authority_level];
+  return `${level.name} ${t`Collection`}`;
+}
+
+export function InfoText({ result }) {
+  const collection = result.getCollection();
+  switch (result.model) {
+    case "card":
+      return jt`Saved question in ${formatCollection(collection)}`;
+    case "collection":
+      return getCollectionInfoText(result.collection);
+    case "database":
+      return t`Database`;
+    case "table":
+      return (
+        <span>
+          {jt`Table in ${(
+            <span>
+              <Database.Link id={result.database_id} />{" "}
+              {result.table_schema && (
+                <Schema.ListLoader
+                  query={{ dbId: result.database_id }}
+                  loadingAndErrorWrapper={false}
+                >
+                  {({ list }) =>
+                    list && list.length > 1 ? (
+                      <span>
+                        <Icon name="chevronright" mx="4px" size={10} />
+                        {/* we have to do some {} manipulation here to make this look like the table object that browseSchema was written for originally */}
+                        <Link
+                          to={Urls.browseSchema({
+                            db: { id: result.database_id },
+                            schema_name: result.table_schema,
+                          })}
+                        >
+                          {result.table_schema}
+                        </Link>
+                      </span>
+                    ) : null
+                  }
+                </Schema.ListLoader>
+              )}
+            </span>
+          )}`}
+        </span>
+      );
+    case "segment":
+    case "metric":
+      return (
+        <span>
+          {result.model === "segment" ? t`Segment of ` : t`Metric for `}
+          <Link to={Urls.tableRowsQuery(result.database_id, result.table_id)}>
+            <Table.Loader id={result.table_id} loadingAndErrorWrapper={false}>
+              {({ table }) =>
+                table ? <span>{table.display_name}</span> : null
+              }
+            </Table.Loader>
+          </Link>
+        </span>
+      );
+    default:
+      return jt`${capitalize(result.model)} in ${formatCollection(collection)}`;
+  }
+}
diff --git a/frontend/src/metabase/search/components/SearchResult.jsx b/frontend/src/metabase/search/components/SearchResult.jsx
index 04da1b6e5cf0638e4d3a4eb1d25e7321ca92d552..587fb4f9d1d2e5b7f448b18b44c11a6a3524a12f 100644
--- a/frontend/src/metabase/search/components/SearchResult.jsx
+++ b/frontend/src/metabase/search/components/SearchResult.jsx
@@ -1,103 +1,24 @@
 /* eslint-disable react/prop-types */
 import React from "react";
 import { Box, Flex } from "grid-styled";
-import styled from "styled-components";
-import { t, jt } from "ttag";
 
-import * as Urls from "metabase/lib/urls";
-import { color, lighten } from "metabase/lib/colors";
-import { capitalize } from "metabase/lib/formatting";
+import { color } from "metabase/lib/colors";
 
 import Icon from "metabase/components/Icon";
-import Link from "metabase/components/Link";
 import Text from "metabase/components/type/Text";
 
-import {
-  PLUGIN_COLLECTIONS,
-  PLUGIN_COLLECTION_COMPONENTS,
-  PLUGIN_MODERATION,
-} from "metabase/plugins";
-
-import Schema from "metabase/entities/schemas";
-import Database from "metabase/entities/databases";
-import Table from "metabase/entities/tables";
-
-const { CollectionAuthorityLevelIcon } = PLUGIN_COLLECTION_COMPONENTS;
+import { PLUGIN_COLLECTIONS, PLUGIN_MODERATION } from "metabase/plugins";
 
-function getColorForIconWrapper(props) {
-  if (props.item.collection_position) {
-    return color("saturated-yellow");
-  }
-  switch (props.type) {
-    case "collection":
-      return lighten("brand", 0.35);
-    default:
-      return color("brand");
-  }
-}
-
-const IconWrapper = styled.div`
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 32px;
-  height: 32px;
-  color: ${getColorForIconWrapper};
-  margin-right: 10px;
-  flex-shrink: 0;
-`;
-
-const ResultLink = styled(Link)`
-  display: block;
-  background-color: transparent;
-  min-height: ${props => (props.compact ? "36px" : "54px")};
-  padding-top: 8px;
-  padding-bottom: 8px;
-  padding-left: 14px;
-  padding-right: ${props => (props.compact ? "20px" : "32px")};
-
-  &:hover {
-    background-color: ${lighten("brand", 0.63)};
-
-    h3 {
-      color: ${color("brand")};
-    }
-  }
-
-  ${Link} {
-    text-underline-position: under;
-    text-decoration: underline ${color("text-light")};
-    text-decoration-style: dashed;
-    &:hover {
-      color: ${color("brand")};
-      text-decoration-color: ${color("brand")};
-    }
-  }
-
-  ${Text} {
-    margin-top: 0;
-    margin-bottom: 0;
-    font-size: 13px;
-    line-height: 19px;
-  }
-
-  h3 {
-    font-size: ${props => (props.compact ? "14px" : "16px")};
-    line-height: 1.2em;
-    word-wrap: break-word;
-    margin-bottom: 0;
-  }
-
-  .Icon-info {
-    color: ${color("text-light")};
-  }
-`;
+import {
+  IconWrapper,
+  ResultLink,
+  Title,
+  TitleWrapper,
+  Description,
+  ContextText,
+} from "./SearchResult.styled";
+import { InfoText } from "./InfoText";
 
-const TitleWrapper = styled.div`
-  display: flex;
-  grid-gap: 0.25rem;
-  align-items: center;
-`;
 const DEFAULT_ICON_SIZE = 20;
 
 function TableIcon() {
@@ -134,154 +55,35 @@ function ItemIcon({ item, type }) {
   );
 }
 
-const CollectionBadgeRoot = styled.div`
-  display: inline-block;
-`;
-
-const CollectionLink = styled(Link)`
-  display: flex;
-  align-items: center;
-  text-decoration: dashed;
-  &:hover {
-    color: ${color("brand")};
-  }
-`;
-
-const AuthorityLevelIcon = styled(CollectionAuthorityLevelIcon).attrs({
-  size: 13,
-})`
-  padding-right: 2px;
-`;
-
-function CollectionBadge({ collection }) {
-  return (
-    <CollectionBadgeRoot>
-      <CollectionLink to={Urls.collection(collection)}>
-        <AuthorityLevelIcon collection={collection} />
-        {collection.name}
-      </CollectionLink>
-    </CollectionBadgeRoot>
-  );
-}
-
-const Title = styled("h3")`
-  margin-bottom: 4px;
-`;
-
 function Score({ scores }) {
   return (
     <pre className="hide search-score">{JSON.stringify(scores, null, 2)}</pre>
   );
 }
-const Context = styled("p")`
-  line-height: 1.4em;
-  color: ${color("text-medium")};
-  margin-top: 0;
-`;
 
-function formatContext(context, compact) {
-  return (
-    !compact &&
-    context && (
-      <Box ml="42px" mt="12px" style={{ maxWidth: 620 }}>
-        <Context>{contextText(context)}</Context>
-      </Box>
-    )
-  );
-}
-
-function formatCollection(collection) {
-  return collection.id && <CollectionBadge collection={collection} />;
-}
-
-const Description = styled(Text)`
-  padding-left: 8px;
-  margin-top: 6px !important;
-  border-left: 2px solid ${lighten("brand", 0.45)};
-`;
-
-function contextText(context) {
-  return context.map(function({ is_match, text }, i) {
-    if (is_match) {
-      return (
-        <strong key={i} style={{ color: color("brand") }}>
-          {" "}
-          {text}
-        </strong>
-      );
-    } else {
-      return <span key={i}> {text}</span>;
-    }
-  });
-}
-
-function getCollectionInfoText(collection) {
-  if (PLUGIN_COLLECTIONS.isRegularCollection(collection)) {
-    return t`Collection`;
+function Context({ context }) {
+  if (!context) {
+    return null;
   }
-  const level = PLUGIN_COLLECTIONS.AUTHORITY_LEVEL[collection.authority_level];
-  return `${level.name} ${t`Collection`}`;
-}
 
-function InfoText({ result }) {
-  const collection = result.getCollection();
-  switch (result.model) {
-    case "card":
-      return jt`Saved question in ${formatCollection(collection)}`;
-    case "collection":
-      return getCollectionInfoText(result.collection);
-    case "database":
-      return t`Database`;
-    case "table":
-      return (
-        <span>
-          {jt`Table in ${(
-            <span>
-              <Database.Link id={result.database_id} />{" "}
-              {result.table_schema && (
-                <Schema.ListLoader
-                  query={{ dbId: result.database_id }}
-                  loadingAndErrorWrapper={false}
-                >
-                  {({ list }) =>
-                    list && list.length > 1 ? (
-                      <span>
-                        <Icon name="chevronright" mx="4px" size={10} />
-                        {/* we have to do some {} manipulation here to make this look like the table object that browseSchema was written for originally */}
-                        <Link
-                          to={Urls.browseSchema({
-                            db: { id: result.database_id },
-                            schema_name: result.table_schema,
-                          })}
-                        >
-                          {result.table_schema}
-                        </Link>
-                      </span>
-                    ) : null
-                  }
-                </Schema.ListLoader>
-              )}
-            </span>
-          )}`}
-        </span>
-      );
-    case "segment":
-    case "metric":
-      return (
-        <span>
-          {result.model === "segment" ? t`Segment of ` : t`Metric for `}
-          <Link to={Urls.tableRowsQuery(result.database_id, result.table_id)}>
-            <Table.Loader id={result.table_id} loadingAndErrorWrapper={false}>
-              {({ table }) =>
-                table ? <span>{table.display_name}</span> : null
-              }
-            </Table.Loader>
-          </Link>
-        </span>
-      );
-    default:
-      return jt`${capitalize(result.model)} in ${formatCollection(collection)}`;
-  }
+  return (
+    <Box ml="42px" mt="12px" style={{ maxWidth: 620 }}>
+      <ContextText>
+        {context.map(({ is_match, text }, i) => {
+          if (!is_match) {
+            return <span key={i}> {text}</span>;
+          }
+
+          return (
+            <strong key={i} style={{ color: color("brand") }}>
+              {" "}
+              {text}
+            </strong>
+          );
+        })}
+      </ContextText>
+    </Box>
+  );
 }
 
 export default function SearchResult({ result, compact }) {
@@ -310,7 +112,7 @@ export default function SearchResult({ result, compact }) {
           <Score scores={result.scores} />
         </Box>
       </Flex>
-      {formatContext(result.context, compact)}
+      {!compact && <Context context={result.context} />}
     </ResultLink>
   );
 }
diff --git a/frontend/src/metabase/search/components/SearchResult.styled.jsx b/frontend/src/metabase/search/components/SearchResult.styled.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..b5341a9ce97cc55facdcf7072d3c501594592bf7
--- /dev/null
+++ b/frontend/src/metabase/search/components/SearchResult.styled.jsx
@@ -0,0 +1,98 @@
+import styled from "styled-components";
+
+import { color, lighten } from "metabase/lib/colors";
+
+import Link from "metabase/components/Link";
+import Text from "metabase/components/type/Text";
+import { space } from "metabase/styled-components/theme";
+
+function getColorForIconWrapper(props) {
+  if (props.item.collection_position) {
+    return color("saturated-yellow");
+  }
+  switch (props.type) {
+    case "collection":
+      return lighten("brand", 0.35);
+    default:
+      return color("brand");
+  }
+}
+
+export const IconWrapper = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 32px;
+  height: 32px;
+  color: ${getColorForIconWrapper};
+  margin-right: 10px;
+  flex-shrink: 0;
+`;
+
+export const ResultLink = styled(Link)`
+  display: block;
+  background-color: transparent;
+  min-height: ${props => (props.compact ? "36px" : "54px")};
+  padding-top: ${space(1)};
+  padding-bottom: ${space(1)};
+  padding-left: 14px;
+  padding-right: ${props => (props.compact ? "20px" : space(3))};
+
+  &:hover {
+    background-color: ${lighten("brand", 0.63)};
+
+    h3 {
+      color: ${color("brand")};
+    }
+  }
+
+  ${Link} {
+    text-underline-position: under;
+    text-decoration: underline ${color("text-light")};
+    text-decoration-style: dashed;
+    &:hover {
+      color: ${color("brand")};
+      text-decoration-color: ${color("brand")};
+    }
+  }
+
+  ${Text} {
+    margin-top: 0;
+    margin-bottom: 0;
+    font-size: 13px;
+    line-height: 19px;
+  }
+
+  h3 {
+    font-size: ${props => (props.compact ? "14px" : "16px")};
+    line-height: 1.2em;
+    word-wrap: break-word;
+    margin-bottom: 0;
+  }
+
+  .Icon-info {
+    color: ${color("text-light")};
+  }
+`;
+
+export const TitleWrapper = styled.div`
+  display: flex;
+  grid-gap: 0.25rem;
+  align-items: center;
+`;
+
+export const ContextText = styled("p")`
+  line-height: 1.4em;
+  color: ${color("text-medium")};
+  margin-top: 0;
+`;
+
+export const Title = styled("h3")`
+  margin-bottom: 4px;
+`;
+
+export const Description = styled(Text)`
+  padding-left: ${space(1)};
+  margin-top: ${space(1)} !important;
+  border-left: 2px solid ${lighten("brand", 0.45)};
+`;