diff --git a/semantic_router/indices/base.py b/semantic_router/indices/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..f5c3e8d6193c33551cf21dc914b65690f80d24d2
--- /dev/null
+++ b/semantic_router/indices/base.py
@@ -0,0 +1,5 @@
+from pydantic.v1 import BaseModel
+
+class BaseIndex(BaseModel):
+
+    pass
\ No newline at end of file
diff --git a/semantic_router/indices/local_index.py b/semantic_router/indices/local_index.py
index 9a72b7efae93dcb3cf5535268bfc67fc73e40a90..784c99673c38c45adf71f01d41438a7e961ced4e 100644
--- a/semantic_router/indices/local_index.py
+++ b/semantic_router/indices/local_index.py
@@ -1,6 +1,8 @@
 import numpy as np
 from typing import List, Any
 from .base import BaseIndex
+from semantic_router.linear import similarity_matrix, top_scores
+from typing import Tuple
 
 class LocalIndex(BaseIndex):
     """
@@ -9,35 +11,32 @@ class LocalIndex(BaseIndex):
 
     def __init__(self):
         self.index = None
-        self.categories = None
 
-    def add(self, items: List[Any], categories: List[str]):
+    def add(self, embeds: List[Any]):
         """
-        Add items to the index with their corresponding categories.
+        Add items to the index.
         """
-        embeds = np.array(items)
+        embeds = np.array(embeds)
         if self.index is None:
             self.index = embeds
-            self.categories = np.array(categories)
         else:
             self.index = np.concatenate([self.index, embeds])
-            self.categories = np.concatenate([self.categories, np.array(categories)])
 
-    def remove(self, category: str):
+    def remove(self, indices_to_remove: List[int]):
         """
         Remove all items of a specific category from the index.
         """
-        if self.categories is not None:
-            indices_to_remove = np.where(self.categories == category)[0]
-            self.index = np.delete(self.index, indices_to_remove, axis=0)
-            self.categories = np.delete(self.categories, indices_to_remove, axis=0)
+        self.index = np.delete(self.index, indices_to_remove, axis=0)
 
-    def search(self, query: Any, top_k: int = 5) -> List[Any]:
+    def is_index_populated(self):
+        return self.index is not None and len(self.index) > 0
+
+    def search(self, query_vector: Any, top_k: int = 5) -> Tuple[np.ndarray, np.ndarray]:
         """
         Search the index for the query and return top_k results.
         """
         if self.index is None:
-            return []
-        sim = np.dot(self.index, query) / (np.linalg.norm(self.index, axis=1) * np.linalg.norm(query))
-        idx = np.argsort(sim)[-top_k:]
-        return [(self.categories[i], sim[i]) for i in idx[::-1]]
\ No newline at end of file
+            raise ValueError("Index is not populated.")
+        sim = similarity_matrix(query_vector, self.index)
+        return top_scores(sim, top_k)
+                          
diff --git a/semantic_router/layer.py b/semantic_router/layer.py
index 08afccfab19b590a1fc975e9325c69d6cb961d0b..487d8b24554bc059e9898d8610fac39ece5186e2 100644
--- a/semantic_router/layer.py
+++ b/semantic_router/layer.py
@@ -11,9 +11,10 @@ from semantic_router.encoders import BaseEncoder, OpenAIEncoder
 from semantic_router.linear import similarity_matrix, top_scores
 from semantic_router.llms import BaseLLM, OpenAILLM
 from semantic_router.route import Route
-from semantic_router.schema import Encoder, EncoderType, RouteChoice
+from semantic_router.schema import Encoder, EncoderType, RouteChoice, Index
 from semantic_router.utils.logger import logger
 
+IndexType = Union[LocalIndex, None]
 
 def is_valid(layer_config: str) -> bool:
     """Make sure the given string is json format and contains the 3 keys: ["encoder_name", "encoder_type", "routes"]"""
@@ -155,15 +156,17 @@ class RouteLayer:
     categories: Optional[np.ndarray] = None
     score_threshold: float
     encoder: BaseEncoder
+    index: IndexType = None
 
     def __init__(
         self,
         encoder: Optional[BaseEncoder] = None,
         llm: Optional[BaseLLM] = None,
         routes: Optional[List[Route]] = None,
+        index_name: Optional[str] = None,
     ):
-        logger.info("Initializing RouteLayer")
-        self.index = None
+        logger.info("local")
+        self.index = Index.get_by_name(index_name="index")
         self.categories = None
         if encoder is None:
             logger.warning(
@@ -281,11 +284,7 @@ class RouteLayer:
             str_arr = np.array([route.name] * len(embeds))
             self.categories = np.concatenate([self.categories, str_arr])
         # create utterance array (the index)
-        if self.index is None:
-            self.index = np.array(embeds)
-        else:
-            embed_arr = np.array(embeds)
-            self.index = np.concatenate([self.index, embed_arr])
+        self.index.add(embeds)
         # add route to routes list
         self.routes.append(route)
 
@@ -301,13 +300,13 @@ class RouteLayer:
             self.routes = [route for route in self.routes if route.name != name]
             logger.info(f"Removed route `{name}`")
             # Also remove from index and categories
-            if self.categories is not None and self.index is not None:
+            if self.categories is not None and self.index.is_index_populated():
                 indices_to_remove = [
                     i
                     for i, route_name in enumerate(self.categories)
                     if route_name == name
                 ]
-                self.index = np.delete(self.index, indices_to_remove, axis=0)
+                self.index.remove(indices_to_remove)
                 self.categories = np.delete(self.categories, indices_to_remove, axis=0)
 
     def _add_routes(self, routes: List[Route]):
@@ -325,14 +324,7 @@ class RouteLayer:
             if self.categories is not None
             else route_array
         )
-
-        # create utterance array (the index)
-        embed_utterance_arr = np.array(embedded_utterance)
-        self.index = (
-            np.concatenate([self.index, embed_utterance_arr])
-            if self.index is not None
-            else embed_utterance_arr
-        )
+        self.index.add(embedded_utterance)
 
     def _encode(self, text: str) -> Any:
         """Given some text, encode it."""
@@ -343,10 +335,9 @@ class RouteLayer:
 
     def _retrieve(self, xq: Any, top_k: int = 5) -> List[dict]:
         """Given a query vector, retrieve the top_k most similar records."""
-        if self.index is not None:
+        if self.index.is_index_populated():
             # calculate similarity matrix
-            sim = similarity_matrix(xq, self.index)
-            scores, idx = top_scores(sim, top_k)
+            scores, idx = self.index.search(xq, top_k)
             # get the utterance categories (route names)
             routes = self.categories[idx] if self.categories is not None else []
             return [{"route": d, "score": s.item()} for d, s in zip(routes, scores)]
diff --git a/semantic_router/schema.py b/semantic_router/schema.py
index 46ee7f590c3275b314b9ecb3e52adc54f16012fe..917055cb69afee66a2f1581b683c013d756b33e7 100644
--- a/semantic_router/schema.py
+++ b/semantic_router/schema.py
@@ -11,6 +11,8 @@ from semantic_router.encoders import (
     OpenAIEncoder,
 )
 
+from semantic_router.indices.local_index import LocalIndex
+
 
 class EncoderType(Enum):
     HUGGINGFACE = "huggingface"
@@ -73,3 +75,13 @@ class DocumentSplit(BaseModel):
     docs: List[str]
     is_triggered: bool = False
     triggered_score: Optional[float] = None
+
+
+class Index:
+    @classmethod
+    def get_by_name(cls, index_name: str):
+        if index_name == "local" or index_name is None:
+            return LocalIndex()
+        # TODO: Later we'll add more index options.
+        else:
+            raise ValueError(f"Invalid index name: {index_name}")
\ No newline at end of file