diff --git a/src/components/query/QueryIVOARegistry.js b/src/components/query/QueryIVOARegistry.js
index aefc08cd8da6c455752413265ff55161b912a11f..1cff633ed033e663714c73d36d84723c04accddb 100644
--- a/src/components/query/QueryIVOARegistry.js
+++ b/src/components/query/QueryIVOARegistry.js
@@ -1,13 +1,15 @@
 import React, { useContext, useEffect } from "react";
 import { useParams } from "react-router-dom";
 import axios from "axios";
-import { Container, Button, Row, Col } from "react-bootstrap";
+import { Container, Button, Row, Col, Form as RBForm } from "react-bootstrap";
 import Form from "react-jsonschema-form";
 import { GlobalContext } from "../../contexts/GlobalContext";
 import { QueryContext } from "../../contexts/QueryContext";
 import QueryResults from "./QueryResults";
 import parseQueryForm from "../../utils/form/parseQueryForm";
 import { IVOAContext } from "../../contexts/IVOAContext";
+import parseVOServiceForm from "../../utils/form/parseVOServiceForm";
+import VOServiceResults from "./VOServiceResults";
 
 export default function QueryIVOARegistry() {
   // queryMap is a map of dictionaries, where each dictionary consists of
@@ -17,7 +19,7 @@ export default function QueryIVOARegistry() {
   //  "results": null}
   const { queryMap, formData, setFormData } = useContext(QueryContext);
   const { config, api_host, setConfigName } = useContext(GlobalContext);
-  const { selectedRegistry } = useContext(IVOAContext);
+  const { selectedRegistry, queryStep, setQueryStep } = useContext(IVOAContext);
   const { uri } = useParams();
   console.log("uri:", uri);
 
@@ -49,11 +51,21 @@ export default function QueryIVOARegistry() {
   }, [uri]);
 
   useEffect(() => {
-    console.log(config.query_schema);
+    console.log("query schema:", config.query_schema);
     if (!formData) return;
     console.log("formData:", formData);
-    let gui = config.query_schema.name;
-    const queries = parseQueryForm(gui, formData);
+    const gui = config.query_schema.name;
+    let queries = [];
+
+    if (queryStep === "run-query") {
+      selectedRegistry.forEach((access_url) => {
+        queries = [...queries, ...parseVOServiceForm(formData, access_url)];
+      });
+    } else {
+      queries = parseQueryForm(gui, formData);
+    }
+
+    console.log("queries:", queries);
 
     // Ideally query for each catalog is sent to ESAP API Gateway, and query results is returned
     // This is under development in the backend at the moment
@@ -113,49 +125,89 @@ export default function QueryIVOARegistry() {
 
   const uiSchemaProp = config.ui_schema ? { uiSchema: config.ui_schema } : {};
   console.log("UI Schema props:", uiSchemaProp);
+  console.log("Form Data:", formData);
 
-  return (
-    <Container fluid>
-      <Form
-        schema={config.query_schema}
-        ObjectFieldTemplate={formTemplate}
-        formData={formData}
-        onSubmit={({ formData }) => setFormData(formData)}
-        {...uiSchemaProp}
-      >
-        <div>
-          <Button type="submit">Get Registry Services</Button>
-        </div>
-      </Form>
-      {Array.from(queryMap.keys()).map((catalog) => {
-        console.log("catalog:", catalog);
-        const details = queryMap.get(catalog);
-        console.log("Details:", details);
-        console.log("Results:", details.results);
-        let catalogName =
-          config.query_schema.properties.catalog.enumNames[
-            config.query_schema.properties.catalog.enum.findIndex(
-              (name) => name === catalog
-            )
-          ];
-        return (
-          <div key={catalog} className="mt-3">
-            <Row>
-              <Col>
-                <h4>List of registries</h4>
-              </Col>
-              <Col>
-                {selectedRegistry.length === 0 ? (
-                  <></>
-                ) : (
-                  <Button type="submit">Query selected registry</Button>
-                )}
-              </Col>
-            </Row>
-            <QueryResults catalog={catalog} />
+  if (queryStep === "run-query") {
+    uiSchemaProp.uiSchema = { query: { "ui:widget": "textarea" } };
+    console.log("new ui schema:", uiSchemaProp);
+    return (
+      <Container fluid>
+        <Form
+          schema={config.query_schema}
+          ObjectFieldTemplate={formTemplate}
+          formData={formData}
+          onSubmit={({ formData }) => setFormData(formData)}
+          {...uiSchemaProp}
+        >
+          <RBForm.Control as="select" multiple>
+            {selectedRegistry.map((registry) => {
+              return <option>{registry}</option>;
+            })}
+          </RBForm.Control>
+          <div>
+            <Button className="mt-3" type="submit">
+              Query Registry
+            </Button>
           </div>
-        );
-      })}
-    </Container>
-  );
+        </Form>
+        {selectedRegistry.map((registry) => {
+          const details = queryMap.get(registry);
+          console.log("Details:", details);
+          return <VOServiceResults catalog={registry} />;
+        })}
+      </Container>
+    );
+  } else {
+    return (
+      <Container fluid>
+        <Form
+          schema={config.query_schema}
+          ObjectFieldTemplate={formTemplate}
+          formData={formData}
+          onSubmit={({ formData }) => setFormData(formData)}
+          {...uiSchemaProp}
+        >
+          <div>
+            <Button type="submit">Get Registry Services</Button>
+          </div>
+        </Form>
+        {Array.from(queryMap.keys()).map((catalog) => {
+          console.log("catalog:", catalog);
+          const details = queryMap.get(catalog);
+          console.log("Details:", details);
+          console.log("Results:", details.results);
+          let catalogName =
+            config.query_schema.properties.catalog.enumNames[
+              config.query_schema.properties.catalog.enum.findIndex(
+                (name) => name === catalog
+              )
+            ];
+          return (
+            <div key={catalog} className="mt-3">
+              <Row>
+                <Col>
+                  <h4>List of registries</h4>
+                </Col>
+                <Col>
+                  {selectedRegistry.length === 0 ? (
+                    <></>
+                  ) : (
+                    <Button
+                      type="submit"
+                      onClick={() => {
+                        setQueryStep("run-query");
+                      }}
+                    >
+                      Query selected registry
+                    </Button>
+                  )}
+                </Col>
+              </Row>
+              <QueryResults catalog={catalog} />
+            </div>
+          );
+        })}
+      </Container>
+    );
+  }
 }
diff --git a/src/components/query/VOServiceResults.js b/src/components/query/VOServiceResults.js
new file mode 100644
index 0000000000000000000000000000000000000000..af5fec62fd8b37b04d8c3f5112ae9e8fc35c09fb
--- /dev/null
+++ b/src/components/query/VOServiceResults.js
@@ -0,0 +1,40 @@
+import React, { useContext } from "react";
+import { Alert, Table } from "react-bootstrap";
+import { QueryContext } from "../../contexts/QueryContext";
+
+export default function VORegistryResults({ catalog }) {
+  const { queryMap } = useContext(QueryContext);
+
+  if (!queryMap.get(catalog)) return null;
+  console.log("VO service queryMap:", queryMap.get(catalog));
+  if (queryMap.get(catalog).status === "fetched") {
+    if (queryMap.get(catalog).results.length === 0)
+      return <Alert variant="warning">No matching results found!</Alert>;
+    return (
+      <div>
+        <h1>Results from {catalog}</h1>
+        <Table className="mt-3" responsive>
+          <thead>
+            <tr className="bg-light">
+              <th>Link to data</th>
+            </tr>
+          </thead>
+          <tbody>
+            {queryMap.get(catalog).results.query_results.map((result) => {
+              return (
+                <tr key={result.result}>
+                  <td>
+                    <a href={result.result} rel="noopener noreferrer" download>
+                      {result.result}
+                    </a>
+                  </td>
+                </tr>
+              );
+            })}
+          </tbody>
+        </Table>
+      </div>
+    );
+  }
+  return null;
+}
diff --git a/src/contexts/IVOAContext.js b/src/contexts/IVOAContext.js
index 03a60bee39284cda3e02a8c08afa413e4febf72f..6064404a513f537fb93444f855f9b879d78aa199 100644
--- a/src/contexts/IVOAContext.js
+++ b/src/contexts/IVOAContext.js
@@ -4,8 +4,25 @@ import useMap from "../hooks/useMap";
 export const IVOAContext = createContext();
 
 export function IVOAContextProvider({ children }) {
-  const [selectedRegistry, setSelectedRegistry] = useState([]);
   const [registryList, setRegistryList] = useState([]);
+  //const [selectedRegistry, setSelectedRegistry] = useState([]);
+  //const [queryStep, setQueryStep] = useState("get-services");
+  /* 
+    IVOA query steps:
+    1. get-services
+    2. run-query: query selected registry
+  */
+
+  // For testing purpose
+  // start manual setup block
+  const [queryStep, setQueryStep] = useState("run-query");
+  const [selectedRegistry, setSelectedRegistry] = useState([
+    // "http://astron.nl/tap",
+    // "http://aip.gavo.org/tap",
+    // "http://archive.stsci.edu/caomtap",
+    "http://vao.stsci.edu/CAOMTAP/TapService.aspx",
+  ]);
+  // end block
 
   function handleAddRegistry(access_url) {
     setSelectedRegistry([...selectedRegistry, access_url]);
@@ -25,6 +42,8 @@ export function IVOAContextProvider({ children }) {
         removeRegistry: handleRemoveRegistry,
         registryList,
         setRegistryList,
+        queryStep,
+        setQueryStep,
       }}
     >
       {children}
diff --git a/src/routes/Routes.js b/src/routes/Routes.js
index 5cea8e2796c348ba82c3f4e7cccb50a87a2ced3d..c1b375ce8bfad0479601fc369c206cd613ed666b 100644
--- a/src/routes/Routes.js
+++ b/src/routes/Routes.js
@@ -44,7 +44,9 @@ export default function Routes() {
         <Route exact path="/archives/:uri" component={ArchiveDetails} />
         <Route exact path="/archives/:uri/query">
           <QueryContextProvider>
-            <QueryCatalogs />
+            <IVOAContextProvider>
+              <QueryCatalogs />
+            </IVOAContextProvider>
           </QueryContextProvider>
         </Route>
       </Switch>
diff --git a/src/utils/form/parseVOServiceForm.js b/src/utils/form/parseVOServiceForm.js
new file mode 100644
index 0000000000000000000000000000000000000000..eba08e89669973ab4b34f26102346630503ad5ca
--- /dev/null
+++ b/src/utils/form/parseVOServiceForm.js
@@ -0,0 +1,40 @@
+export default function ParseVOServiceForm(formData, access_url) {
+  let queries = [];
+  // queries is an array of dictionaries, where each dictionary consists of
+  // {"catalog": "catalogname",
+  //  "esapquery": "querystring"}
+  let query = "";
+  let formInput = Object.entries(formData);
+  console.log(formInput);
+
+  // IVOA query consists of multiple steps
+  // Step 1: get list of registry services
+
+  for (let [key, value] of formInput) {
+    console.log(`${key}: ${value}`);
+    if (value && value !== "all" && key !== "catalog") {
+      query += `${`${query}` ? "&" : ""}` + key + "=" + value;
+    }
+  }
+  query += `${`${query}` ? "&" : ""}` + "access_url=" + access_url;
+
+  console.log("Query:", query);
+  // If catalog is set to "all", query for each catalog needs to be generated {"catalog": "catalogname",
+  //  "catalogquery": "querystring",
+  //  "status": "null|fetching|fetched",
+  //  "results": null}
+  let catalog = formInput.find(([key]) => key === "catalog")[1];
+  let service_type = formInput.find(([key]) => key === "service_type")[1];
+
+  let esapquery =
+    "run-query/?" + query + `${`${query}` ? "&" : ""}dataset_uri=` + catalog;
+
+  queries.push({
+    catalog: access_url,
+    service_type: service_type,
+    esapquery: esapquery,
+  });
+
+  console.log("Queries:", queries);
+  return queries;
+}