VitalSync 

VitalSync

SnapView Dynamic Lists – User Guide 

Bring live data to any site with SnapView Dynamic Lists

SnapView lets you drop live, query-driven content into any website — not just Ontraport Pages or specific plan tiers. When the visual editor hits its limits, SnapView’s Dynamic List renders the results of a VitalSync query right inside your HTML. Build the query once in VitalSync, paste its ID into your page, and you’re done.
Create a related-articles sidebar, an image gallery, KPI cards, or a fully featured data table. If you’re comfortable with HTML/CSS (and optionally a little JS), you can design almost anything.

This is an advanced feature aimed at users who can work with HTML/CSS. We provide the embed script, required attributes, and copy-paste examples to get you moving fast. Our support covers setup and query behavior; we can’t troubleshoot fully custom code.

What you’ll need
  • A VitalSync Query ID (puid)
  • Your entity slug
  • Your API key

Add our embed script to the header, then include an HTML element with three required attributes plus any modifications you want to make or merge fields you want to render.

TL;DR: Design anywhere, power it with a VitalSync query, and render it with a tiny HTML tag
1) What is a SnapView Dynamic List?
A Dynamic List renders records returned by a VitalSync query directly inside your Ontraport Page block. You can:
  • Repeat your own HTML template once per record, or
  • Let SnapView render a table automatically.
You create and test the query in VitalSync, copy its Query ID, then paste it in the page element.
2) Before you start
  1. Build your query in VitalSync (filters, joins, computed fields, sorting, variables, etc.).
  2. Copy the Query ID (puid).
  3. Know your entity slug and API key.
  4. Add our universal embed script in your page header:

<script async src="https://static-au03.vitalstats.app/static/dynamic-list/v1/latest.js" crossorigin="anonymous"></script>
  
3) The three required attributes
Every Dynamic List element needs all three:
  • data-dynamic-list – the Query ID (puid)
  • data-entity – your VitalSync entity slug
  • data-entity-key – your API key
These identify which query to run and authenticate the request.
4) Quick start examples
4.1 Custom HTML template (non‑table)

<div
  data-dynamic-list="YOUR_QUERY_ID"
  data-entity="your-entity-slug"
  data-entity-key="YOUR_API_KEY"
  data-limit="10">
  <div>
    <p>Date: [date] | Count: [count] | Total: [total]</p>
  </div>
</div>
  
Example 4.1: The most basic version of adding your data to the page without any complex styling

Date: [date] | Count: [count] | Total: [total]

4.2 Custom HTML template (table)

<div
  data-dynamic-list="YOUR_QUERY_ID"
  data-entity="your-entity-slug"
  data-entity-key="YOUR_API_KEY"
  data-table="true"></div>
  
Example 4.2:
5) How rendering works
  • On load, the script locates every element with data-dynamic-list and adds a loading class while it renders.
  • Non‑table: Your element’s inner HTML is used as a template and repeated for each record; tokens like [field] are replaced with values from the query output.
  • Table: If data-table="true", SnapView renders a DataGrid-backed table for you. Columns come from the query’s selection aliases.
  • You can bind a refresh action to any element via data-refresh-button-selector.
6) Attribute reference
Required
  • data-dynamic-list – Query ID (puid)
  • data-entity – Entity slug
  • data-entity-key – API key
General (optional)
  • data-table: set to "true" to render as a table; omit/anything else to use your template.
  • data-op: set to "subscribe" to keep results live via subscription.
  • data-limit: set to a numerical value for client‑side cap on rendered rows.
  • data-offset: set to a numerical value for starting offset (advanced; typically drive from query if needed).
  • data-vars-group: Variables group name to use when the query defines variables (defaults to Default Group).
  • data-refresh-button-selector: CSS selector for a clickable element to re-run the query and re-render (not used for subscribe).
Table‑only (optional)
  • data-theme-mode: light | dark | auto (match user’s device).
  • data-auto-height: true to grow table to fit up to the current limit (no scrollbars).
Modifiers for variables and properties (optional)
  • data-var-<query-variable>: Provide a value for a query variable.
    • For camelCase variables use dashes: data-var-start-date="2025-01-01".
  • data-url-var-<query-variable>: Pull a variable value from URL params.
    • Example: ?type=weekly → data-url-var-type="type".
    • URL overrides data-var-* if present.
  • data-url-prop-<attribute>: Override an element attribute from the URL.
    • Example: ?renderTable=true → data-url-prop-table="renderTable".
6.1 Using the subscription for live data
This example uses the "Subscribe" option so that as live data updates it will automaticall update on to this page without having to refresh. The example shows is count of contacts.

<div
  data-dynamic-list="YOUR_QUERY_ID"
  data-entity="your-entity-slug"
  data-entity-key="YOUR_API_KEY"
  data-op="subscribe">
  <H1>[countOfContacts]</H1>
</div>
  
Example 6.1:

[countOfContacts]

6.2 Using the url variable modifier for number of days of data
This example allows a change of days being queried based on a X_DAYS variable. When a value of "-30" is passed to this variable it is getting data from the last 30 days.

<div
  data-dynamic-list="YOUR_QUERY_ID"
  data-entity="your-entity-slug"
  data-entity-key="YOUR_API_KEY"
  data-url-var-X_DAY="days">
  <H1>[countOfContacts]</H1>
</div>
  
Example 6.2: in the URL you add?days={a negative number like -5} to change the filter dynamically. Using the buttons below to see how this can be done using Ontraport buttons

[countOfContacts]

7) Using MUI's react data grid to improve the look of tables
When using SnapView you get access to the whole library of abilities that MUI React-data-grid components allow.
Here is an example of some basic modifers.
Tips for non-developers
  • Use Chat GPT to help you write the code for styling using MUI components
  • Take screenshots of the way you want the table to look when writing your prompts to help in showing how you want it to look.
7.1 Table of data styled for specific columns
This example allows a change of days being queried based on a X_DAYS variable. When a value of "-30" is passed to this variable it is getting data from the last 30 days.

<div
  data-dynamic-list="YOUR_QUERY_ID"
  data-entity="your-entity-slug"
  data-entity-key="YOUR_API_KEY"
  data-table="true"
  data-theme-mode="light"
  data-auto-height="true"
  data-limit="10"
  data-init-cb-name="initPotencyBars">
</div>
  

Below is an example of the Footer script — MUI DataGrid custom cell renderers that was generated by Chat GPT 5 with the following prompt with an screenshot of the way I wanted it to look copied from the MUI page here :

------
I want to use MUI React components to make this SnapView table look better.

Here is the current SnapView short code:
{add your short code here}

Please update it so that two specific columns (for example, Column 1 name and Column 2 name) display with conditional styling based on their values:

  • Green if the value is greater than 20%

  • Orange if the value is between 10% and 20%

  • Red if the value is less than 10%

I’d like you to provide the updated code that:

  1. Uses MUI DataGrid features with custom cell rendering for those columns.

  2. Includes any CSS styles or helper functions needed to make the bars or labels display correctly.

Clearly labels the parts of the code that are specific to this table, so I can adapt them later for other queries or columns
​​​​​​​-------
Please note that the below script won't work with your own code but you may use this to assist in your prompt to give your AI assistant examples.


<script>


  window.initPotencyBars = function (dynamicList) {
    const React = window.vitalStatsReact;  // Exposed by SnapView runtime


    function clamp01(n){ return Math.max(0, Math.min(1, n)); }
    function toPct(n){
      if (n == null || isNaN(n)) return 0;
      return Math.max(0, Math.min(100, Number(n)));
    }
    function colorFor(valuePct){


      if (valuePct > 20) return '#1db954';      // green
      if (valuePct >= 10) return '#d59a2a';     // orange/gold
      return '#e23d37';                         // red
    }


    function Bar({ value }) {
      const pct = toPct(value);
      const width = clamp01(pct / 100) * 100;
      const fill = colorFor(pct);
      return React.createElement(
        'div', { className: 'sv-pot-bar' },
        React.createElement(
          'div', { className: 'sv-pot-track' },
          React.createElement('div', {
            className: 'sv-pot-fill',
            style: { width: width + '%', background: fill }
          })
        ),
        React.createElement('div', { className: 'sv-pot-label' }, pct.toFixed(0) + ' %')
      );
    }


    const MATCH_NAMES = new Set(['THC Percentage', 'CBD Percentage']);

    dynamicList.tableCtx
      .setGetRenderCell(function (item /* selectionInfo item */, ctx) {

        if (MATCH_NAMES.has(item.displayName)) {

          return function (params) {
            return React.createElement(Bar, { value: params.value });
          };
        }

        return undefined;
      })

      .setFinalizeColumns(function (cols, ctx) {
        return cols.map(function (c) {
          if (MATCH_NAMES.has(c.headerName)) {

            c.minWidth = 160;
          }
          return c;
        });
      })

      .setDataGridProps({
        density: 'comfortable'
      });


  };
</script>
  
Example 7.1: The below table is the outcome of the styling applied using the MUI react components with a script and conditional styling generated by Chat GPT 5.0
8) Grid Mode with custom HTML/CSS
This is list of Courses for a Learning Management system. We are getting the name, code, image and description and then used Chat GPT 5 to generate the responsive stying that we wanted based on a prompt that used an image from another LMS system that we wanted to look like.

<style>
  /* ===== Base grid (desktop/tablet) ===== */
  .sv-grid {
    display: grid !important;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 20px;
    align-items: stretch;
    width: 100% !important;
  }
  @media (max-width: 1024px) {
    .sv-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }

  /* ===== Mobile = horizontal slider ===== */
  @media (max-width: 640px) {
    .sv-grid {
      display: flex !important;
      overflow-x: auto !important;
      -webkit-overflow-scrolling: touch;
      scroll-snap-type: x mandatory;
      gap: 16px;
      padding: 0 12px 8px;
      width: 100% !important;
    }
    .sv-card {
      box-sizing: border-box;
      flex: 0 0 calc(100vw - 48px);
      max-width: calc(100vw - 48px);
      scroll-snap-align: start;
    }
    .sv-grid::-webkit-scrollbar { display: none; }
    .sv-grid { scrollbar-width: none; }
  }

  /* ===== Card ===== */
  .sv-card {
    display: flex;
    flex-direction: column;
    background: #fff;
    border-radius: 16px;
    box-shadow: 0 2px 10px rgba(0,0,0,.06), 0 1px 4px rgba(0,0,0,.06);
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    transition: transform .15s ease, box-shadow .15s ease;
    height: 100%;
  }
  .sv-card:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,.10); }
  .sv-grid > * { min-width: 0; }

  /* ===== Media sizing guardrails ===== */
  .sv-card__img,
  .sv-card img {
    display: block;
    width: 100% !important;
    max-width: 100% !important;
    height: auto !important;
    aspect-ratio: 16 / 9;
    object-fit: cover;
  }

  .sv-card__body { padding: 16px; display: flex; flex-direction: column; gap: 8px; flex: 1 1 auto; }
  .sv-card__code { font-weight: 600; font-size: .9rem; letter-spacing: .02em; opacity: .9; }
  .sv-card__title { font-size: 1.15rem; line-height: 1.25; margin: 0; font-weight: 700; }
  .sv-card__desc {
    margin: 0; font-size: .95rem; line-height: 1.45; color: #333;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
  }
  .sv-card__cta { margin-top: auto; padding: 0 16px 16px; display: flex; align-items: center; gap: 8px; font-weight: 600; }
  .sv-card__arrow { width: 22px; height: 22px; flex: 0 0 22px; transition: transform .15s ease; }
  .sv-card:hover .sv-card__arrow { transform: translateX(4px); }
</style>

<div
  class="sv-grid"
  data-dynamic-list="YOUR_QUERY_ID"
  data-entity="your-entity-slug"
  data-entity-key="YOUR_API_KEY">

  <a class="sv-card" href="#">
    <img class="sv-card__img" src="[image]" alt="" loading="lazy" />
    <div class="sv-card__body">
      <div class="sv-card__code">[code]</div>
      <h3 class="sv-card__title"></h3>
      <p class="sv-card__desc">[description]</p>
    </div>
    <div class="sv-card__cta">
      <span>Explore</span>
      <svg class="sv-card__arrow" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        <path d="M5 12h14M13 5l7 7-7 7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>
    </div>
  </a>
</div>
  
8.1 Grid view of courses
This snippet shows you how to render your SnapView dynamic list as a responsive grid of cards that adapts across devices. On desktop it displays up to four cards per row, while on mobile it automatically becomes a swipeable slider with one card per screen.
Tips
  • Column labels & order are derived from the query’s selectionInfo.
  • For advanced control (pagination, sorting, column renderers), use the table context hooks

Visualise Data Over Time with Chart JS

[Date]: [Count]
Daily totals & 7-day moving average

Table View with MUI react data grids

 🎯 Ready to Snap
 

 Stay ahead with a fully integrated feed inside your Ontraport account — simple to launch, easy to manage, and built to grow with you. 
arrow_forward
View Demo
check
Expert-built installation
check
 Seamless VitalSync integration 
check
   Fast Delivery & Setup   

VitalSync

Transform your business decisions with AI-driven insights from accurately structured and seamlessly integrated data.
Contact Us
13 Parakeet Place Mullumbimby NSW 2482
+61420908066
info@itmooti.com

arrow_drop_down_circle
Divider Text
© 2025 IT Mooti Pty Ltd. All rights reserved.
[bot_catcher]