Custom Data Passthrough

What Is Custom Data Passthrough?

TLDR; Custom Data Passthrough allows you to define variables that will be passed to the Tether dashboard with any feedback submitted after variables are defined.

You and your team know your application best. You know what information about your users and pages is important to you. With Custom Data Passthrough, you can pass that information along to Tether, ensuring submitted feedback has as much useful and actionable context as possible.

For example, let's say your user model has an is_subscribed attribute. One of your subscriber benefits is feedback priority, so you want to pass that information along with any feedback so that it can be used to prioritize items in the Tether dashboard.

All Custom Data fields can be viewed in the Tether dashboard on the "Custom Data" tab of the feedback item's technical details.

We're also working on some exciting new features that rely on Data Passthrough functionality to do some pretty cool stuff - more on that soon!

How To Define Custom Data

Tether offers two different ways for you to define Data Passthrough variables, either by adding new attributes to your Tether installation script tag, or by defining new object properties on the global tetherSettings object. The script-tag method is the easiest way to get started, but the tetherSettings object is more flexible and allows you to define variables dynamically and update them on-the-fly until the moment feedback is submitted.

Option 1 - Script Tag Attributes

Define your Custom Data attribute on the Tether install script by simply prefixing your attributes with data-custom-. The entire attribute name should be lowercase, and use kebab-case for multi-word attributes. These kebab-case variable names will be converted to camel-case on the backend. For example, if you wanted to pass through a variable named isSubscribed, you would add the attribute data-custom-is-subscribed to your Tether script tag.

{alert.fa-exclamation-circle} The data-custom- prefix is required for all Data Passthrough variables defined in the script tag. If you forget to add the prefix, the variable will not be passed through to Tether.


{info.fa-info-circle} This script tag method is best suited for static variables that are known at page load, and will not change thereafter. If you need to pass through dynamic variables or update variables on-the-fly, you should use the tetherSettings method documented below.



<script
    defer
    type="text/javascript"
    src="https://app.tethered.dev/js/tether.js"
    data-id="tether-client"
    data-token="****************************************"
    data-project-uuid="********-****-****-****-************"

    data-custom-is-subscribed="true"
></script>


Of course, in its current state, this example is not very useful. We can do better.

Rather than a hard-coded true/false value we can use interpolation, if supported by your framework or language, to pass through a more dynamic value. Let's also track what subscription plan the user is on for additional context when reviewing feedback.


For example, if you were using Laravel Blade, you could do something like this:


<html>
<head>
    ...
    <script
        defer
        type="text/javascript"
        src="https://app.tethered.dev/js/tether.js"
        data-id="tether-client"
        data-token="****************************************"
        data-project-uuid="********-****-****-****-************"

        data-custom-is-subscribed="{{ Auth::user()->is_subscribed }}"
        data-custom-subscription-plan="{{ Auth::user()->subscription->plan_id }}"
        data-custom-is-admin="{{ Auth::user()->is_admin }}"
    ></script>
</head>
...
</html>

Using React:

import React from 'react';

// The authenticated user object could be passed as a prop, retrieved from global state, extracted from session, etc.
const DataPassthroughExample = ({ user }) => {
    return (
        <>
            <script
                defer
                type="text/javascript"
                src="https://app.tethered.dev/js/tether.js"
                data-id="tether-client"
                data-token="****************************************"
                data-project-uuid="********-****-****-****-************"

                data-custom-is-subscribed="{ user.isSubscribed }"
                data-custom-subscription-plan="{ user.subscription.planId }"
                data-custom-is-admin="{ user.isAdmin }"
            ></script>

            ...
        </>
    );
};

export default DataPassthroughExample;

Using Vue:


<template>
    <script
        defer
        type="text/javascript"
        src="https://app.tethered.dev/js/tether.js"
        data-id="tether-client"
        data-token="****************************************"
        data-project-uuid="********-****-****-****-************"

        data-custom-is-subscribed="{{ user.isSubscribed }}"
        data-custom-subscription-plan="{{ user.subscription.planId }}"
        data-custom-is-admin="{{ user.isAdmin }}"
    ></script>

    ...
</template>

<script>
    export default {
        props: {
            user: {
                type: Object,
                required: true,
            },
        },
    }
</script>

Option 2 - The tetherSettings Object

The second option for setting Custom Data is to define properties on the global tetherSettings.customData object. This option is more flexible and allows you to define variables dynamically using Javascript at any time up until the moment feedback is submitted. This is also the preferred method for defining Custom Data if you are using a stack that does not support HTML interpolation or server-side rendering.

Since this method has all the power of Javascript behind it, you can do some pretty cool stuff. For example, you could define a property on the tetherSettings.customData based on the result of an API request, when the user clicks a button, submits a form, or scrolls the page. The possibilities are only limited by your imagination.

{info.fa-info-circle} For some browsers or browser versions, you may need to access Tether settings via the window object window.tetherSettings.customData


{info.fa-info-circle} Note that customData is persisted for the duration of the user's session. Upon reloading the page, customData properties are restored. In order to clear or remove a customData property, it must be explicitly reassigned, or deleted:

delete tetherSettings.customData.myProperty


Using plain Javascript:


<html>
<head>
    ...
    <script
        defer
        type="text/javascript"
        src="https://app.tethered.dev/js/tether.js"
        data-id="tether-client"
        data-token="****************************************"
        data-project-uuid="********-****-****-****-************"
    ></script>

    <script>
        window.tetherSettings.customData = {
            isSubscribed: true,
            subscriptionPlan: 'Pro',
            isAdmin: false,
        };

        window.addEventListener('tether.feedback.submitted', () => {
            // Increment feedbackCount every time the user submits feedback
            if (!window.tetherSettings.customData['feedbackCount']) {
                window.tetherSettings.customData['feedbackCount'] = 1;
            }
            else {
                window.tetherSettings.customData.feedbackCount++;
            }
        });
    </script>
</head>
...
</html>

Here's an example using React:

import React, { useEffect, useState } from 'react';

const DataPassthroughExample = () => {
    const [isSubscribed, setIsSubscribed] = useState(false);

    useEffect(() => {
        // Make an API request to get the user's subscription status
        fetch('/api/user/subscription')
            .then(response => response.json())
            .then(data => {
                // Update the `window.tetherSettings` object with the result
                window.tetherSettings.customData['isSubscribed'] = data.subscription.active;
            });
    }, []);

    return (
        <div>
            <div>
                <button onClick={ () => setIsSubscribed(true) }>Subscribe</button>
                <button onClick={ () => setIsSubscribed(false) }>Unsubscribe</button>
            </div>

            ...
        </div>
    );
};

export default DataPassthroughExample;

Using Vue:


<template>
    <div>
        <div>
            <button @click="isSubscribed = true">Subscribe</button>
            <button @click="isSubscribed = false">Unsubscribe</button>
        </div>

        ...
    </div>
</template>

<script>
    export default {
        data() {
            return {
                isSubscribed: false,
            };
        },

        mounted() {
            // Make an API request to get the user's subscription status
            fetch('/api/user/subscription')
                .then(response => response.json())
                .then(data => {
                    // Update the `tetherSettings.customData` object with the result
                    window.tetherSettings.customData['isSubscribed'] = data.subscription.active;
                });
        },
    }
</script>

{info.fa-info-circle} When both Custom Data methods are used, their values will be merged - with precedence given to the properties defined in tetherSettings.customData when there are conflicts.