diff --git a/src/components/Interactive.js b/src/components/Interactive.js
index 9ba219db463f3dc15c3ff14fea76442992a3a822..b3508f3beaf43e391098e289588ba751960d6a54 100644
--- a/src/components/Interactive.js
+++ b/src/components/Interactive.js
@@ -2,5 +2,16 @@ import React from "react";
 import { Alert } from "react-bootstrap";
 
 export default function Interactive() {
-  return <Alert variant="info">This service is under development!</Alert>;
+  return (
+    <Alert variant="warning">
+      <p>You will leave ESAP GUI and be redirected to</p>
+      <a
+        target="_blank"
+        rel="noopener noreferrer"
+        href="http://130.246.212.44/escape/"
+      >
+        Interactive analysis platform hosted by SKAO
+      </a>
+    </Alert>
+  );
 }
diff --git a/src/components/query/ASTRONVOResults.js b/src/components/query/ASTRONVOResults.js
index 7bd41157f72f5448803752f9e445745b833a4314..1a5735b385b6690444b403f2acf21d0739a5773a 100644
--- a/src/components/query/ASTRONVOResults.js
+++ b/src/components/query/ASTRONVOResults.js
@@ -8,9 +8,9 @@ export default function ASTRONVOResults({ catalog }) {
   const { queryMap } = useContext(QueryContext);
   if (!queryMap) return null;
   if (queryMap.get(catalog).status === "fetched") {
-    if (queryMap.get(catalog).results.query_results.length === 0)
+    if (queryMap.get(catalog).results.results.length === 0)
       return <Alert variant="warning">No matching results found!</Alert>;
-    console.log(queryMap.get(catalog).results.query_results);
+    console.log(queryMap.get(catalog).results.results);
     return (
       <>
         <Table className="mt-3" responsive>
@@ -32,7 +32,7 @@ export default function ASTRONVOResults({ catalog }) {
             </tr>
           </thead>
           <tbody>
-            {queryMap.get(catalog).results.query_results.map((result) => {
+            {queryMap.get(catalog).results.results.map((result) => {
               return (
                 <tr key={result.result}>
                   {/* <th>
diff --git a/src/components/query/ApertifResults.js b/src/components/query/ApertifResults.js
index 358b874ea05ff21ff46b5787d572ecac4c37ce1d..b360f12e6653e6da0080e96407dcd16fbfc4f24f 100644
--- a/src/components/query/ApertifResults.js
+++ b/src/components/query/ApertifResults.js
@@ -8,12 +8,9 @@ export default function ApertifResults({ catalog }) {
   const { queryMap } = useContext(QueryContext);
   if (!queryMap) return null;
   if (queryMap.get(catalog).status === "fetched") {
-    if (queryMap.get(catalog).results.query_results.length === 0)
+    if (queryMap.get(catalog).results.results.length === 0)
       return <Alert variant="warning">No matching results found!</Alert>;
-    console.log(
-      "Query results:",
-      queryMap.get(catalog).results.query_results["query_results"]
-    );
+    console.log("Query results:", queryMap.get(catalog).results.results);
     return (
       <>
         <Table className="mt-3" responsive>
@@ -35,7 +32,7 @@ export default function ApertifResults({ catalog }) {
             </tr>
           </thead>
           <tbody>
-            {queryMap.get(catalog).results.query_results.map((result) => {
+            {queryMap.get(catalog).results.results.map((result) => {
               return (
                 <tr key={result.PID}>
                   {/* <th>
diff --git a/src/components/query/QueryIVOARegistry.js b/src/components/query/QueryIVOARegistry.js
index c373f93c039dd2b6eeb538483efd98d140df9848..cf1101a5d9266a9e42c8ac3a5eb7ca5e00351250 100644
--- a/src/components/query/QueryIVOARegistry.js
+++ b/src/components/query/QueryIVOARegistry.js
@@ -1,12 +1,15 @@
 import React, { useContext, useEffect } from "react";
 import { useParams } from "react-router-dom";
 import axios from "axios";
-import { Container, Button } 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
@@ -16,6 +19,7 @@ export default function QueryIVOARegistry() {
   //  "results": null}
   const { queryMap, formData, setFormData } = useContext(QueryContext);
   const { config, api_host, setConfigName } = useContext(GlobalContext);
+  const { selectedRegistry, queryStep, setQueryStep } = useContext(IVOAContext);
   const { uri } = useParams();
   console.log("uri:", uri);
 
@@ -47,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
@@ -59,6 +73,7 @@ export default function QueryIVOARegistry() {
     queries.forEach((query) => {
       queryMap.set(query.catalog, {
         catalog: query.catalog,
+        service_type: query.service_type,
         esapquery: query.esapquery,
         status: "fetching",
         results: null,
@@ -69,6 +84,7 @@ export default function QueryIVOARegistry() {
         .then((queryResponse) => {
           queryMap.set(query.catalog, {
             catalog: query.catalog,
+            service_type: query.service_type,
             esapquery: query.esapquery,
             status: "fetched",
             results: queryResponse.data,
@@ -77,6 +93,7 @@ export default function QueryIVOARegistry() {
         .catch(() => {
           queryMap.set(query.catalog, {
             catalog: query.catalog,
+            service_type: query.service_type,
             esapquery: query.esapquery,
             status: "error",
             results: null,
@@ -108,38 +125,99 @@ 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">
-            <h4>List of registries</h4>
-            <QueryResults catalog={catalog} />
+  if (queryStep === "run-query") {
+    uiSchemaProp.uiSchema = {
+      query: { "ui:widget": "textarea" },
+      keyword: { "ui:widget": "hidden" },
+      tap_schema: { "ui:widget": "hidden" },
+    };
+    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 (
+            <div className="mt-3">
+              <VOServiceResults catalog={registry} />
+            </div>
+          );
+        })}
+      </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>
+              <div className="mt-3">
+                <QueryResults catalog={catalog} />
+              </div>
+            </div>
+          );
+        })}
+      </Container>
+    );
+  }
 }
diff --git a/src/components/query/VORegistryResults.js b/src/components/query/VORegistryResults.js
index 828a9ae7670a24aa25c01f894bb8cc982cc28e84..d8481c93430700d4c8ef622cc70a238703f9fa6c 100644
--- a/src/components/query/VORegistryResults.js
+++ b/src/components/query/VORegistryResults.js
@@ -7,12 +7,18 @@ import { IVOAContext } from "../../contexts/IVOAContext";
 
 export default function VORegistryResults({ catalog }) {
   const { queryMap } = useContext(QueryContext);
-  const { registryList, add, remove } = useContext(IVOAContext);
+  const {
+    selectedRegistry,
+    addRegistry,
+    removeRegistry,
+    registryList,
+    setRegistryList,
+  } = useContext(IVOAContext);
   // const [checkAll, setCheckAll] = useState("");
 
   useEffect(() => {
-    console.log("RegistryList:", registryList);
-  }, [registryList]);
+    console.log("Selected Registry:", selectedRegistry);
+  }, [selectedRegistry]);
 
   // useEffect(() => {
   //   console.log("checkAll:", checkAll);
@@ -24,7 +30,10 @@ export default function VORegistryResults({ catalog }) {
   if (queryMap.get(catalog).status === "fetched") {
     if (queryMap.get(catalog).results.results.length === 0)
       return <Alert variant="warning">No matching results found!</Alert>;
+
     console.log("VO Registry results:", queryMap.get(catalog).results.results);
+    setRegistryList(queryMap.get(catalog).results.results);
+    console.log("Registry List:", registryList);
 
     return (
       <>
@@ -40,12 +49,12 @@ export default function VORegistryResults({ catalog }) {
                   //     ? queryMap
                   //         .get(catalog)
                   //         .results.results.map((result) => {
-                  //           add(result.access_url);
+                  //           addRegistry(result.access_url);
                   //         })
                   //     : queryMap
                   //         .get(catalog)
                   //         .results.results.map((result) => {
-                  //           remove(result.access_url);
+                  //           removeRegistry(result.access_url);
                   //         });
                   // }}
                   />
@@ -70,8 +79,8 @@ export default function VORegistryResults({ catalog }) {
                         onChange={(event) => {
                           console.log(event.target.checked);
                           event.target.checked
-                            ? add(result.access_url)
-                            : remove(result.access_url);
+                            ? addRegistry(result.access_url)
+                            : removeRegistry(result.access_url);
                         }}
                       />
                     </InputGroup>
@@ -87,7 +96,7 @@ export default function VORegistryResults({ catalog }) {
             })}
           </tbody>
         </Table>
-        <Paginate />
+        {/* <Paginate /> */}
       </>
     );
   } else {
diff --git a/src/components/query/VOServiceResults.js b/src/components/query/VOServiceResults.js
new file mode 100644
index 0000000000000000000000000000000000000000..0f9ee8c79bc649c1c5e2b91202a8dcea1c09145d
--- /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.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.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/components/query/ZooniverseResults.js b/src/components/query/ZooniverseResults.js
index f8814e4c5c3ab1cbc3c802750534604d9aa26591..69ff383d90af05b207d7549bfd45f6db2558ed42 100644
--- a/src/components/query/ZooniverseResults.js
+++ b/src/components/query/ZooniverseResults.js
@@ -1,25 +1,25 @@
-import React, { useContext, useState} from "react";
+import React, { useContext, useState } from "react";
 import { Table, Alert } from "react-bootstrap";
 import { QueryContext } from "../../contexts/QueryContext";
 import LoadingSpinner from "../LoadingSpinner";
 import Paginate, { pagination_fields } from "../Paginate";
 
 const DATETIME_OPTIONS = {
-  year: 'numeric',
-  month: 'long',
-  day: 'numeric',
-  hour: 'numeric',
-  minute: 'numeric',
-  second: 'numeric',
+  year: "numeric",
+  month: "long",
+  day: "numeric",
+  hour: "numeric",
+  minute: "numeric",
+  second: "numeric",
   hour12: false,
-  timeZoneName: 'short'
+  timeZoneName: "short",
 };
 
-Object.isObject = function(obj) {
-    return (obj && obj.constructor === this) || false;
+Object.isObject = function (obj) {
+  return (obj && obj.constructor === this) || false;
 };
 
-function renderArray(array, currentReactKey=""){
+function renderArray(array, currentReactKey = "") {
   return array.map((element, index) => {
     const updatedReactKey = `${currentReactKey}_${index}`;
     const separator = index < array.length - 1 ? ", " : "";
@@ -27,97 +27,132 @@ function renderArray(array, currentReactKey=""){
   });
 }
 
-function renderObject(object, currentReactKey=""){
+function renderObject(object, currentReactKey = "") {
   return (
     <Table key={currentReactKey + "_objTable"}>
-    <tbody>
-    {Object.entries(object).map(([key, value]) =>{
-      const updatedReactKey=`${currentReactKey}_${key}`;
-      return(
-        <tr key={updatedReactKey}><td className="b">{key}</td><td>{renderIfCompound(value, updatedReactKey)}</td></tr>
-      )
-    })}
-    </tbody>
+      <tbody>
+        {Object.entries(object).map(([key, value]) => {
+          const updatedReactKey = `${currentReactKey}_${key}`;
+          return (
+            <tr key={updatedReactKey}>
+              <td className="b">{key}</td>
+              <td>{renderIfCompound(value, updatedReactKey)}</td>
+            </tr>
+          );
+        })}
+      </tbody>
     </Table>
-  )
+  );
 }
 
-function renderIfCompound(element, currentReactKey="", separatorForPod="", floatPrecision=3) {
+function renderIfCompound(
+  element,
+  currentReactKey = "",
+  separatorForPod = "",
+  floatPrecision = 3
+) {
   if (Array.isArray(element)) {
     return renderArray(element, currentReactKey, separatorForPod);
   } else if (Object.isObject(element)) {
     return renderObject(element, currentReactKey, separatorForPod);
   } else if (typeof element === "boolean") {
-    return JSON.stringify(element) + separatorForPod
-  } else if (Number.isInteger(element)){
+    return JSON.stringify(element) + separatorForPod;
+  } else if (Number.isInteger(element)) {
     return element.toString() + separatorForPod;
   } else {
-    try{
+    try {
       return element.toFixed(floatPrecision) + separatorForPod;
-    }
-    catch(err){
-      return  `${element}` + separatorForPod
+    } catch (err) {
+      return `${element}` + separatorForPod;
     }
   }
 }
 
 function titleCase(string) {
-      var sentence = string.toLowerCase().split(" ");
-      for(var i = 0; i< sentence.length; i++){
-         sentence[i] = sentence[i][0].toUpperCase() + sentence[i].slice(1);
-      }
-   return sentence.join(" ");
-   }
+  var sentence = string.toLowerCase().split(" ");
+  for (var i = 0; i < sentence.length; i++) {
+    sentence[i] = sentence[i][0].toUpperCase() + sentence[i].slice(1);
+  }
+  return sentence.join(" ");
+}
 
-function newPageCallback(setPage){
+function newPageCallback(setPage) {
   return (args) => {
-      if(args.target){
-        setPage(parseFloat(args.target.text));
-      }
+    if (args.target) {
+      setPage(parseFloat(args.target.text));
+    }
   };
 }
 
-function ZooniverseProjectResults(context){
-  const { queryMap, page, setPage }  = context;
-  const date_formatter=new Intl.DateTimeFormat("default", DATETIME_OPTIONS);
+function ZooniverseProjectResults(context) {
+  const { queryMap, page, setPage } = context;
+  const date_formatter = new Intl.DateTimeFormat("default", DATETIME_OPTIONS);
   const result = queryMap.get("zooniverse_projects").results.query_results[0];
   const numPages = result.pages;
-  const mandatory_fields = ["launch_date", "created_at", "live", "updated_at", "project_id", "display_name", "slug"];
-  const remaining_fields = Object.keys(result).filter(key => !(mandatory_fields.includes(key) || pagination_fields.includes(key)));
+  const mandatory_fields = [
+    "launch_date",
+    "created_at",
+    "live",
+    "updated_at",
+    "project_id",
+    "display_name",
+    "slug",
+  ];
+  const remaining_fields = Object.keys(result).filter(
+    (key) =>
+      !(mandatory_fields.includes(key) || pagination_fields.includes(key))
+  );
   const remaining_headers = remaining_fields.map((field) => {
-    const title=titleCase(field.replace("_", " "));
-    return (<th key={`project_header_${field}`}>{title}</th>);
+    const title = titleCase(field.replace("_", " "));
+    return <th key={`project_header_${field}`}>{title}</th>;
   });
-    return (
-      <>
-      <Paginate getNewPage={newPageCallback(setPage)} currentPage={page} numAdjacent={3} numPages={numPages}/>
-        <Table className="mt-3" responsive>
-          <thead>
-            <tr className="bg-light">
-              {/* <th>
+  return (
+    <>
+      <Paginate
+        getNewPage={newPageCallback(setPage)}
+        currentPage={page}
+        numAdjacent={3}
+        numPages={numPages}
+      />
+      <Table className="mt-3" responsive>
+        <thead>
+          <tr className="bg-light">
+            {/* <th>
               <InputGroup>
                 <InputGroup.Checkbox />
               </InputGroup>
             </th> */}
-              <th>ID</th>
-              <th>Display Name</th>
-              <th>Created</th>
-              <th>Updated</th>
-              <th>Launched</th>
-              <th>Live</th>
-              <th>View</th>
-              {remaining_headers}
-            </tr>
-          </thead>
-          <tbody>
-            {queryMap.get("zooniverse_projects").results.query_results.map((result) => {
-              const launch_date = result.launch_date ? date_formatter.format(new Date(result.launch_date)) : "Not Launched";
-              const created_at = date_formatter.format(new Date(result.created_at));
-              const updated_at = date_formatter.format(new Date(result.updated_at));
-              const live = result.live ? "Yes" : "No"
+            <th>ID</th>
+            <th>Display Name</th>
+            <th>Created</th>
+            <th>Updated</th>
+            <th>Launched</th>
+            <th>Live</th>
+            <th>View</th>
+            {remaining_headers}
+          </tr>
+        </thead>
+        <tbody>
+          {queryMap
+            .get("zooniverse_projects")
+            .results.query_results.map((result) => {
+              const launch_date = result.launch_date
+                ? date_formatter.format(new Date(result.launch_date))
+                : "Not Launched";
+              const created_at = date_formatter.format(
+                new Date(result.created_at)
+              );
+              const updated_at = date_formatter.format(
+                new Date(result.updated_at)
+              );
+              const live = result.live ? "Yes" : "No";
               const remaining_cells = remaining_fields.map((field) => {
                 const reactKey = `project_${result.project_id}_${field}`;
-                return (<td key={reactKey}>{renderIfCompound(result[field], reactKey)}</td>);
+                return (
+                  <td key={reactKey}>
+                    {renderIfCompound(result[field], reactKey)}
+                  </td>
+                );
               });
               return (
                 <tr key={`project_${result.project_id}`}>
@@ -132,84 +167,122 @@ function ZooniverseProjectResults(context){
                   <td>{updated_at}</td>
                   <td>{launch_date}</td>
                   <td>{live}</td>
-                  <td><a href={`https://zooniverse.org/projects/${result.slug}`}>Link</a></td>
+                  <td>
+                    <a href={`https://zooniverse.org/projects/${result.slug}`}>
+                      Link
+                    </a>
+                  </td>
                   {remaining_cells}
                 </tr>
               );
             })}
-          </tbody>
-        </Table>
-        <Paginate getNewPage={newPageCallback(setPage)} currentPage={page} numAdjacent={3} numPages={numPages}/>
-      </>
-    );
-
+        </tbody>
+      </Table>
+      <Paginate
+        getNewPage={newPageCallback(setPage)}
+        currentPage={page}
+        numAdjacent={3}
+        numPages={numPages}
+      />
+    </>
+  );
 }
 
-function ZooniverseWorkflowResults(context){
-  const { queryMap, page, setPage }  = context;
-  let date_formatter=new Intl.DateTimeFormat("default", DATETIME_OPTIONS);
+function ZooniverseWorkflowResults(context) {
+  const { queryMap, page, setPage } = context;
+  let date_formatter = new Intl.DateTimeFormat("default", DATETIME_OPTIONS);
   let result = queryMap.get("zooniverse_workflows").results.query_results[0];
   let result_workflow = result.workflows[0];
   const numPages = result.pages;
-  let mandatory_fields = ["created_at", "updated_at", "workflow_id", "display_name"];
-  let remaining_fields = Object.keys(result_workflow).filter(key => !(mandatory_fields.includes(key) || pagination_fields.includes(key)));
+  let mandatory_fields = [
+    "created_at",
+    "updated_at",
+    "workflow_id",
+    "display_name",
+  ];
+  let remaining_fields = Object.keys(result_workflow).filter(
+    (key) =>
+      !(mandatory_fields.includes(key) || pagination_fields.includes(key))
+  );
   let remaining_headers = remaining_fields.map((field) => {
-    let title=titleCase(field.replace("_", " "));
-    return (<th key={`project_header_${field}`}>{title}</th>);
+    let title = titleCase(field.replace("_", " "));
+    return <th key={`project_header_${field}`}>{title}</th>;
   });
-    return (
-      <>
-      <Paginate getNewPage={newPageCallback(setPage)} currentPage={page} numAdjacent={3} numPages={numPages}/>
-      {queryMap.get("zooniverse_workflows").results.query_results.map((project) => {
-        return (<div key={project.project_id}>
-        <h4>{project.display_name}</h4>
-        <Table className="mt-3" responsive>
-          <thead>
-            <tr className="bg-light">
-              {/* <th>
+  return (
+    <>
+      <Paginate
+        getNewPage={newPageCallback(setPage)}
+        currentPage={page}
+        numAdjacent={3}
+        numPages={numPages}
+      />
+      {queryMap
+        .get("zooniverse_workflows")
+        .results.query_results.map((project) => {
+          return (
+            <div key={project.project_id}>
+              <h4>{project.display_name}</h4>
+              <Table className="mt-3" responsive>
+                <thead>
+                  <tr className="bg-light">
+                    {/* <th>
               <InputGroup>
                 <InputGroup.Checkbox />
               </InputGroup>
             </th> */}
-              <th>ID</th>
-              <th>Display Name</th>
-              <th>Created</th>
-              <th>Updated</th>
-              {remaining_headers}
-              {/* <th>View</th> */}
-            </tr>
-          </thead>
-            <tbody>
-              {project.workflows.map((workflow) => {
-                let created_at = date_formatter.format(new Date(workflow.created_at));
-                let updated_at = date_formatter.format(new Date(workflow.updated_at));
-                let remaining_cells = remaining_fields.map((field) => {
-                  let reactKey = `workflow_${workflow.workflow_id}_${field}`;
-                  return (<td key={reactKey}>{renderIfCompound(workflow[field], reactKey)}</td>);
-                });
-                return (
-                  <tr key={`workflow_${workflow.workflow_id}`}>
-                    {/* <th>
+                    <th>ID</th>
+                    <th>Display Name</th>
+                    <th>Created</th>
+                    <th>Updated</th>
+                    {remaining_headers}
+                    {/* <th>View</th> */}
+                  </tr>
+                </thead>
+                <tbody>
+                  {project.workflows.map((workflow) => {
+                    let created_at = date_formatter.format(
+                      new Date(workflow.created_at)
+                    );
+                    let updated_at = date_formatter.format(
+                      new Date(workflow.updated_at)
+                    );
+                    let remaining_cells = remaining_fields.map((field) => {
+                      let reactKey = `workflow_${workflow.workflow_id}_${field}`;
+                      return (
+                        <td key={reactKey}>
+                          {renderIfCompound(workflow[field], reactKey)}
+                        </td>
+                      );
+                    });
+                    return (
+                      <tr key={`workflow_${workflow.workflow_id}`}>
+                        {/* <th>
                     <InputGroup>
                       <InputGroup.Checkbox />
                     </InputGroup>
                   </th> */}
-                    <td>{workflow.workflow_id}</td>
-                    <td>{workflow.display_name}</td>
-                    <td>{created_at}</td>
-                    <td>{updated_at}</td>
-                    {remaining_cells}
-                    {/* <td><a href={`https://zooniverse.org/workflows/${workflow.slug}`}>Link</a></td> */}
-                  </tr>
-                );
-              })}
-            </tbody>
-          </Table>
-          </div>);
+                        <td>{workflow.workflow_id}</td>
+                        <td>{workflow.display_name}</td>
+                        <td>{created_at}</td>
+                        <td>{updated_at}</td>
+                        {remaining_cells}
+                        {/* <td><a href={`https://zooniverse.org/workflows/${workflow.slug}`}>Link</a></td> */}
+                      </tr>
+                    );
+                  })}
+                </tbody>
+              </Table>
+            </div>
+          );
         })}
-      <Paginate getNewPage={newPageCallback(setPage)} currentPage={page} numAdjacent={3} numPages={numPages}/>
-      </>
-    );
+      <Paginate
+        getNewPage={newPageCallback(setPage)}
+        currentPage={page}
+        numAdjacent={3}
+        numPages={numPages}
+      />
+    </>
+  );
 }
 
 export default function ZooniverseResults({ catalog }) {
@@ -218,13 +291,11 @@ export default function ZooniverseResults({ catalog }) {
   if (context.queryMap.get(catalog).status === "fetched") {
     if (context.queryMap.get(catalog).results.query_results.length === 0)
       return <Alert variant="warning">No matching results found!</Alert>;
-    else if (catalog === "zooniverse_projects"){
+    else if (catalog === "zooniverse_projects") {
       return ZooniverseProjectResults(context);
-    }
-    else if (catalog === "zooniverse_workflows"){
+    } else if (catalog === "zooniverse_workflows") {
       return ZooniverseWorkflowResults(context);
-    }
-    else {
+    } else {
       return <Alert variant="warning">Unrecognised Zooniverse Catalog!</Alert>;
     }
   } else {
diff --git a/src/contexts/GlobalContext.js b/src/contexts/GlobalContext.js
index e18b2eb607345a265f8060ccbf209a52e6d63753..d8c42dcfbba472643df6364b482f3c14dfd3a148 100644
--- a/src/contexts/GlobalContext.js
+++ b/src/contexts/GlobalContext.js
@@ -5,10 +5,10 @@ import getCookie from "../utils/getCookie";
 export const GlobalContext = createContext();
 
 export function GlobalContextProvider({ children }) {
-  console.log("ASTRON ESAP version 14 aug 2020");
+  console.log("ASTRON ESAP version ", Date());
   const api_host =
     process.env.NODE_ENV === "development"
-      ? "http://localhost:15671/esap-api/"
+      ? "http://sdc.astron.nl:5555/esap-api/"
       : "/esap-api/";
 
   const [config, setConfig] = useState();
diff --git a/src/contexts/IVOAContext.js b/src/contexts/IVOAContext.js
index eba8707e13090945b095b033a21600474fd60d29..a5363958e05f9505b8046e3577721360e5c4b50d 100644
--- a/src/contexts/IVOAContext.js
+++ b/src/contexts/IVOAContext.js
@@ -1,25 +1,49 @@
 import React, { createContext, useState } from "react";
+import useMap from "../hooks/useMap";
 
 export const IVOAContext = createContext();
 
 export function IVOAContextProvider({ children }) {
   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) {
-    setRegistryList([...registryList, access_url]);
+    setSelectedRegistry([...selectedRegistry, access_url]);
   }
 
   function handleRemoveRegistry(access_url) {
-    const copy = [...registryList];
+    const copy = [...selectedRegistry];
     const index = copy.findIndex((url) => url === access_url);
     copy.splice(index, 1);
-    setRegistryList(copy);
+    setSelectedRegistry(copy);
   }
   return (
     <IVOAContext.Provider
       value={{
+        selectedRegistry,
+        addRegistry: handleAddRegistry,
+        removeRegistry: handleRemoveRegistry,
         registryList,
-        add: handleAddRegistry,
-        remove: handleRemoveRegistry,
+        setRegistryList,
+        queryStep,
+        setQueryStep,
       }}
     >
       {children}
diff --git a/src/routes/Routes.js b/src/routes/Routes.js
index 5cea8e2796c348ba82c3f4e7cccb50a87a2ced3d..cc4098e741edcc92f1ea27b75432113034c25f56 100644
--- a/src/routes/Routes.js
+++ b/src/routes/Routes.js
@@ -42,6 +42,13 @@ export default function Routes() {
         <Route exact path="/login" component={handleLogin} />
         <Route exact path="/logout" component={handleLogout} />
         <Route exact path="/archives/:uri" component={ArchiveDetails} />
+        <Route exact path="/archives/ivoa/query">
+          <QueryContextProvider>
+            <IVOAContextProvider>
+              <QueryIVOARegistry />
+            </IVOAContextProvider>
+          </QueryContextProvider>
+        </Route>
         <Route exact path="/archives/:uri/query">
           <QueryContextProvider>
             <QueryCatalogs />
diff --git a/src/utils/form/parseADEXForm.js b/src/utils/form/parseADEXForm.js
index d2f9460dc23fc6ea74995f1f051a881a8590194f..351fabef237b4d6f60208855092efa1e64e4060b 100644
--- a/src/utils/form/parseADEXForm.js
+++ b/src/utils/form/parseADEXForm.js
@@ -6,7 +6,7 @@ export default function ParseADEXForm(formData) {
   //  "esapquery": "querystring"}
   let query = "";
   let formInput = Object.entries(formData);
-  console.log(formInput);
+  console.log("formInput:", formInput);
 
   for (let [key, value] of formInput) {
     console.log(`${key}: ${value}`);
diff --git a/src/utils/form/parseASTRONVOForm.js b/src/utils/form/parseASTRONVOForm.js
index 8724fc5aabc2c2c9d2a6cda65687d5f6d5b78b1d..9162c828a9635cabe750e5dbbcccfd933dc8ea4c 100644
--- a/src/utils/form/parseASTRONVOForm.js
+++ b/src/utils/form/parseASTRONVOForm.js
@@ -5,7 +5,7 @@ export default function ParseASTRONVOForm(formData) {
   //  "esapquery": "querystring"}
   let query = "";
   let formInput = Object.entries(formData);
-  console.log(formInput);
+  console.log("formInput:", formInput);
 
   for (let [key, value] of formInput) {
     console.log(`${key}: ${value}`);
diff --git a/src/utils/form/parseIVOAForm.js b/src/utils/form/parseIVOAForm.js
index 640227870591c5f3dbbcb864355cc7240c5fb2df..e518ff0ccaf91f1b5730b7f98bdc8fe9df92bef7 100644
--- a/src/utils/form/parseIVOAForm.js
+++ b/src/utils/form/parseIVOAForm.js
@@ -22,12 +22,14 @@ export default function ParseIVOAForm(formData) {
   //  "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 =
     "get-services/?" + query + `${`${query}` ? "&" : ""}dataset_uri=` + catalog;
 
   queries.push({
     catalog: catalog,
+    service_type: service_type,
     esapquery: esapquery,
   });
 
diff --git a/src/utils/form/parseVOServiceForm.js b/src/utils/form/parseVOServiceForm.js
new file mode 100644
index 0000000000000000000000000000000000000000..c42aec9a71378c01d5b44ba9d9e675ff0b09abf9
--- /dev/null
+++ b/src/utils/form/parseVOServiceForm.js
@@ -0,0 +1,42 @@
+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}`);
+    // Ignore value of keyword from get-services query, in this step keyword will not be used.
+    if (value && value !== "all" && key !== "catalog" && key !== "keyword") {
+      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 =
+    "query/?" + query + `${`${query}` ? "&" : ""}dataset_uri=` + catalog;
+
+  queries.push({
+    catalog: access_url,
+    service_type: service_type,
+    esapquery: esapquery,
+  });
+
+  console.log("Queries:", queries);
+  return queries;
+}