diff --git a/App.jsx b/App.jsx
index a2bfb66..5da8698 100644
--- a/App.jsx
+++ b/App.jsx
@@ -1,6 +1,7 @@
 import { BrowserRouter } from "react-router-dom";
 import { NavigationMenu } from "@shopify/app-bridge-react";
 import Routes from "./Routes";
+import React from "react";
 
 import {
   AppBridgeProvider,
@@ -21,8 +22,12 @@ export default function App() {
             
+      {option}
+     
+  ));
+
+  const optionsMarkup =
+    options.length > 0
+      ? options.map((option) => {
+          const { label, value } = option;
+
+          return (
+            
+              {label}
+             
+          );
+        })
+      : null;
+
+  const loadingMarkup = loading ? 
+        {optionsMarkup && !loading ? optionsMarkup : null}
+        {loadingMarkup}
+       
+    ) : null;
+
+  return (
+    
+      
+        {tagsMarkup} 
+      
+      
}
+            onChange={updateText}
+            label="Search tags to add or remove from products"
+            labelHidden
+            value={inputValue}
+            placeholder="Search tags to add or remove from products"
+            onFocus={getProductTags}
+          />
+        }
+      >
+        {listboxMarkup}
+      
+    
 setToastProps(emptyToastProps)} />
-  );
-
-  const handlePopulate = async () => {
-    setIsLoading(true);
-    const response = await fetch("/api/products/create");
-
-    if (response.ok) {
-      await refetchProductCount();
-      setToastProps({ content: "5 products created!" });
-    } else {
-      setIsLoading(false);
-      setToastProps({
-        content: "There was an error creating products",
-        error: true,
-      });
-    }
-  };
-
-  return (
-    <>
-      {toastMarkup}
-      
-        
-          
-            Sample products are created with a default title and price. You can
-            remove them at any time.
-          
-          
-            TOTAL PRODUCTS
-            
-              
-                {isLoadingCount ? "-" : data.count}
-               
-             
-           
-         
-       
-    >
-  );
-}
diff --git a/components/ProductsTable.jsx b/components/ProductsTable.jsx
new file mode 100644
index 0000000..147bcde
--- /dev/null
+++ b/components/ProductsTable.jsx
@@ -0,0 +1,86 @@
+import {
+  IndexTable,
+  LegacyCard,
+  LegacyStack,
+  useIndexResourceState,
+  Text,
+  Badge,
+  EmptySearchResult,
+} from "@shopify/polaris";
+import React from "react";
+
+export default function ProductsTable({
+  productsArray,
+  addTags,
+  removeTags,
+  isLoading = false,
+  tagsToUpdate,
+}) {
+  const { selectedResources, allResourcesSelected, handleSelectionChange } =
+    useIndexResourceState(productsArray);
+
+  const promotedBulkActions = [
+    {
+      content: "Add selected tags",
+      onAction: () => addTags(selectedResources),
+    },
+    {
+      content: "Remove selected tags",
+      onAction: () => removeTags(selectedResources),
+    },
+  ];
+
+  const rowMarkup = productsArray.map(({ id, title, tags }, index) => (
+    
+      
+        
+          {title}
+         
+       
+      
+        
+          {tags.map((tag) => (
+            {tag} 
+          ))}
+         
+       
+     
+  ));
+
+  return (
+    
+       
+  );
+}
+
+function EmptyState() {
+  return (
+    
+      
+        
+          
+            Nice work on building a Shopify app 🎉 
+            
+              Your app is ready to explore! It contains everything you need to
+              get started including the{" "}
+              
+            
+              Ready to go? Explore the{" "}
+              
+            
+              Learn more about building your app in{" "}
+              
+           
+         
+        
+          
+            
+         
+       
+     
+  );
+}
 
 export default function HomePage() {
+  const navigate = useNavigate();
+
   return (
     
-       navigate("/product-tagger"),
+        }}
+      />
       
         
-          
-            
-              
-                
-                  Nice work on building a Shopify app 🎉 
-                  
-                    Your app is ready to explore! It contains everything you
-                    need to get started including the{" "}
-                    
-                  
-                    Ready to go? Start populating your app with some sample
-                    products to view and test in your store.{" "}
-                  
-                  
-                    Learn more about building out your app in{" "}
-                    
-                 
-               
-              
-                
-                  
-               
-             
-           
-         
-        
-           
        
       
diff --git a/pages/product-tagger.jsx b/pages/product-tagger.jsx
new file mode 100644
index 0000000..e2e6d30
--- /dev/null
+++ b/pages/product-tagger.jsx
@@ -0,0 +1,110 @@
+import { Page, Layout } from "@shopify/polaris";
+import { TitleBar, ResourcePicker, useToast } from "@shopify/app-bridge-react";
+
+import { useAuthenticatedFetch } from "../hooks/useAuthenticatedFetch";
+import ProductsTable from "../components/ProductsTable";
+import ProductTagsInput from "../components/ProductTagsInput";
+
+import React, { useState } from "react";
+
+export default function HomePage() {
+  // Custom implementation of fetch that adds Shopify auth
+  const fetch = useAuthenticatedFetch();
+  const { show } = useToast();
+  const [productSelectorOpen, setProductSelectorOpen] = useState(false);
+  const [productsTableData, setProductsTableData] = useState([]);
+  const [tagsToUpdate, setTagsToUpdate] = useState([]);
+  const [isLoading, setIsLoading] = useState(false);
+
+  function handleProductSelection(selectPayload) {
+    setProductsTableData(selectPayload.selection);
+    setProductSelectorOpen(false);
+  }
+
+  async function addTags(params) {
+    setIsLoading(true);
+    const response = await fetch("/api/producttags", {
+      method: "POST",
+      body: JSON.stringify({ products: params, tags: tagsToUpdate }),
+      headers: { "Content-Type": "application/json" },
+    });
+    setIsLoading(false);
+
+    if (response?.ok) {
+      show("Tags added successfully, they may not show immediately");
+    } else {
+      show("Error adding tags, please try again", { isError: true });
+      console.error("error response", response);
+    }
+  }
+
+  async function removeTags(params) {
+    setIsLoading(true);
+    const response = await fetch("/api/producttags", {
+      method: "DELETE",
+      body: JSON.stringify({ products: params, tags: tagsToUpdate }),
+      headers: { "Content-Type": "application/json" },
+    });
+    setIsLoading(false);
+
+    if (response?.ok) {
+      show("Tags removed successfully, they may still show for a while");
+    } else {
+      show("Error removing tags, please try again", { isError: true });
+      console.error("error response", response);
+    }
+  }
+
+  async function getTags() {
+    const response = await fetch("/api/producttags", {
+      method: "GET",
+    });
+
+    if (response?.ok) {
+      const data = await response.json();
+      return data?.tags || [];
+    } else {
+      show("Error getting tags", { isError: true });
+      console.error("error response", response);
+    }
+  }
+
+  return (
+    
+       setProductSelectorOpen(true),
+        }}
+      />
+      
+        
+           
+        
+           
+       
+
+      {/* Resource picker opens as a modal via App Bridge */}
+       setProductSelectorOpen(false)}
+        onSelection={handleProductSelection}
+      />
+       
+  );
+}