Embed a Survey

To embed a SenseFolks survey, load its package, add the matching custom element, and set survey-key. Follow the example for your frontend.

How do I embed a survey in my frontend?

Vanilla JS / HTML

Add a module script and custom element:

html
<!DOCTYPE html>
<html>
<head>
  <title>My Page</title>
  <!-- Load the component -->
  <script type="module" src="https://unpkg.com/@sensefolks/[email protected]/dist/sf-fastpoll/sf-fastpoll.esm.js"></script>
</head>
<body>
  <h2>We'd love your feedback</h2>
  
  <!-- Place the survey -->
  <sf-fastpoll 
    survey-key="your-survey-uuid"
    completion-message="Thanks for your feedback!">
  </sf-fastpoll>
</body>
</html>

Use type="module". Components require the modern browsers listed in their references; there is no classic-script fallback.

Script URLs by Survey Type

Match the script to your survey type:

ComponentScript URL
sf-fastpollhttps://unpkg.com/@sensefolks/[email protected]/dist/sf-fastpoll/sf-fastpoll.esm.js
sf-userchoicehttps://unpkg.com/@sensefolks/[email protected]/dist/sf-userchoice/sf-userchoice.esm.js
sf-pricepointhttps://unpkg.com/@sensefolks/[email protected]/dist/sf-pricepoint/sf-pricepoint.esm.js
sf-openfeedbackhttps://unpkg.com/@sensefolks/[email protected]/dist/sf-openfeedback/sf-openfeedback.esm.js
sf-featurepriorityhttps://unpkg.com/@sensefolks/[email protected]/dist/sf-featurepriority/sf-featurepriority.esm.js
sf-reactionhttps://unpkg.com/@sensefolks/[email protected]/dist/sf-reaction/sf-reaction.esm.js

React + Next.js

Load the package when the React component mounts:

jsx
// SurveyComponent.jsx
import { useEffect } from 'react';

export function SurveyComponent({ surveyKey }) {
  useEffect(() => {
    // Import the component on mount
    import('@sensefolks/fastpoll');
  }, []);

  return (
    <div className="survey-wrapper">
      <h2>Quick Poll</h2>
      <sf-fastpoll 
        survey-key={surveyKey}
        completion-message="Thanks for voting!" 
      />
    </div>
  );
}

In Next.js, use 'use client' and a dynamic import:

tsx
// components/Survey.tsx
'use client';

import { useEffect } from 'react';

interface SurveyProps {
  surveyKey: string;
  type?: 'fastpoll' | 'pricepoint' | 'userchoice' | 'featurepriority' | 'openfeedback' | 'reaction';
}

export function Survey({ surveyKey, type = 'fastpoll' }: SurveyProps) {
  useEffect(() => {
    // Dynamic import for client-side only
    import(`@sensefolks/${type}`);
  }, [type]);

  const Tag = `sf-${type}` as keyof JSX.IntrinsicElements;

  return (
    <Tag 
      survey-key={surveyKey}
      completion-message="Thank you for your feedback!"
    />
  );
}

// Usage in a page:
// <Survey surveyKey="your-uuid" type="fastpoll" />

Works with the App Router and Pages Router.

TypeScript Support

Declare the custom elements:

typescript
// types/sf-components.d.ts
declare namespace JSX {
  interface IntrinsicElements {
    'sf-fastpoll': React.DetailedHTMLProps<
      React.HTMLAttributes<HTMLElement> & {
        'survey-key': string;
        'completion-message'?: string;
      },
      HTMLElement
    >;
    'sf-pricepoint': React.DetailedHTMLProps<
      React.HTMLAttributes<HTMLElement> & {
        'survey-key': string;
        'completion-message'?: string;
      },
      HTMLElement
    >;
    'sf-userchoice': React.DetailedHTMLProps<
      React.HTMLAttributes<HTMLElement> & {
        'survey-key': string;
        'completion-message'?: string;
      },
      HTMLElement
    >;
    'sf-featurepriority': React.DetailedHTMLProps<
      React.HTMLAttributes<HTMLElement> & {
        'survey-key': string;
        'completion-message'?: string;
      },
      HTMLElement
    >;
    'sf-openfeedback': React.DetailedHTMLProps<
      React.HTMLAttributes<HTMLElement> & {
        'survey-key': string;
        'completion-message'?: string;
      },
      HTMLElement
    >;
    'sf-reaction': React.DetailedHTMLProps<
      React.HTMLAttributes<HTMLElement> & {
        'survey-key': string;
      },
      HTMLElement
    >;
  }
}

Save as types/sf-components.d.ts and include it in tsconfig.json.

Vue + Nuxt

vue
<!-- SurveyComponent.vue -->
<template>
  <div class="survey-wrapper">
    <h2>Quick Poll</h2>
    <sf-fastpoll 
      :survey-key="surveyKey"
      completion-message="Thanks for voting!" 
    />
  </div>
</template>

<script setup lang="ts">
import { onMounted } from 'vue';

defineProps<{
  surveyKey: string;
}>();

onMounted(() => {
  import('@sensefolks/fastpoll');
});
</script>

Vite Configuration

Configure Vue to recognise sf-* elements:

typescript
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          // Treat sf-* tags as custom elements
          isCustomElement: (tag) => tag.startsWith('sf-')
        }
      }
    })
  ]
});

Nuxt Client Plugin

Use a client-only plugin and <ClientOnly>:

vue
// plugins/sensefolks.client.ts
export default defineNuxtPlugin(async () => {
  await import('@sensefolks/fastpoll');
});

// Example usage in a page/component
<template>
  <ClientOnly>
    <sf-fastpoll
      :survey-key="surveyKey"
      completion-message="Thanks for voting!"
    />
  </ClientOnly>
</template>

Angular

Add the custom elements schema:

typescript
// app.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  schemas: [CUSTOM_ELEMENTS_SCHEMA], // Required for custom elements
  bootstrap: [AppComponent]
})
export class AppModule {}

Load the survey in your component:

typescript
// survey.component.ts
import { Component, Input, OnInit } from '@angular/core';

@Component({
  selector: 'app-survey',
  template: `
    <div class="survey-wrapper">
      <h2>Quick Poll</h2>
      <sf-fastpoll 
        [attr.survey-key]="surveyKey"
        [attr.completion-message]="completionMessage">
      </sf-fastpoll>
    </div>
  `
})
export class SurveyComponent implements OnInit {
  @Input() surveyKey: string = '';
  completionMessage = 'Thanks for voting!';

  ngOnInit() {
    import('@sensefolks/fastpoll');
  }
}

Bind dynamic keys with [attr.survey-key].

Svelte

svelte
<!-- Survey.svelte -->
<script lang="ts">
  import { onMount } from 'svelte';
  
  export let surveyKey: string;
  export let type: 'fastpoll' | 'pricepoint' | 'userchoice' | 'featurepriority' | 'openfeedback' | 'reaction' = 'fastpoll';
  
  onMount(async () => {
    await import(`@sensefolks/${type}`);
  });
</script>

<div class="survey-wrapper">
  {#if type === 'fastpoll'}
    <sf-fastpoll 
      survey-key={surveyKey}
      completion-message="Thanks!">
    </sf-fastpoll>
  {:else if type === 'pricepoint'}
    <sf-pricepoint 
      survey-key={surveyKey}
      completion-message="Thanks!">
    </sf-pricepoint>
  {:else if type === 'userchoice'}
    <sf-userchoice 
      survey-key={surveyKey}
      completion-message="Thanks!">
    </sf-userchoice>
  {/if}
</div>

Use the custom element directly in Svelte:

Astro

astro
---
// Survey.astro
interface Props {
  surveyKey: string;
  type?: 'fastpoll' | 'pricepoint' | 'userchoice' | 'featurepriority' | 'openfeedback' | 'reaction';
}

const { surveyKey, type = 'fastpoll' } = Astro.props;
const componentUrls = {
  fastpoll: 'https://unpkg.com/@sensefolks/[email protected]/dist/sf-fastpoll/sf-fastpoll.esm.js',
  pricepoint: 'https://unpkg.com/@sensefolks/[email protected]/dist/sf-pricepoint/sf-pricepoint.esm.js',
  userchoice: 'https://unpkg.com/@sensefolks/[email protected]/dist/sf-userchoice/sf-userchoice.esm.js',
  featurepriority: 'https://unpkg.com/@sensefolks/[email protected]/dist/sf-featurepriority/sf-featurepriority.esm.js',
  openfeedback: 'https://unpkg.com/@sensefolks/[email protected]/dist/sf-openfeedback/sf-openfeedback.esm.js',
  reaction: 'https://unpkg.com/@sensefolks/[email protected]/dist/sf-reaction/sf-reaction.esm.js',
};
const componentUrl = componentUrls[type];
---

<div class="survey-wrapper">

  <sf-fastpoll 
    survey-key={surveyKey}
    completion-message="Thank you!">
  </sf-fastpoll>
</div>
<script define:vars={{ componentUrl }}>
  // Load component dynamically
  import(componentUrl);
</script>

Load the package in a client-side script:

Optional Embed Patterns

Conditional Loading

Load the component when its section becomes visible:

javascript
// Show survey based on user action
function showSurveyOnScroll() {
  const surveyContainer = document.getElementById('survey');
  
  window.addEventListener('scroll', () => {
    // Show survey when user scrolls 50% down the page
    const scrollPercent = (window.scrollY / document.body.scrollHeight) * 100;
    
    if (scrollPercent > 50 && !surveyContainer.hasChildNodes()) {
      // Dynamically create and insert survey
      const survey = document.createElement('sf-fastpoll');
      survey.setAttribute('survey-key', 'your-survey-uuid');
      surveyContainer.appendChild(survey);
    }
  }, { once: true });
}

Custom Styling

Style the exposed CSS Parts:

css
/* Custom styling for embedded survey */
.survey-wrapper {
  max-width: 600px;
  margin: 2rem auto;
  padding: 1.5rem;
  border-radius: 12px;
  background: #f8fafc;
}

/* Style the survey component */
sf-fastpoll::part(survey-container) {
  font-family: inherit;
}

sf-fastpoll::part(button) {
  background: #6366f1;
  border-radius: 8px;
}

sf-fastpoll::part(choice-option) {
  border-radius: 8px;
  transition: all 0.2s;
}

sf-fastpoll::part(choice-option):hover {
  border-color: #6366f1;
}

If the Survey Does Not Work

The component does not render

  • Ensure the script tag has type="module"
  • Check that survey-key is the UUID from your dashboard
  • Verify the component is loaded before it's used (use dynamic imports)
  • If the survey defines Session Data, assign the completesessionData object before setting the survey key. See theSession Data guidefor the required sequence and validation errors.

TypeScript does not recognise the custom element

  • Add the type declarations shown in the React section
  • For Vue, configure isCustomElement in your build config

Your styles do not apply

  • Use ::part() selectors — regular CSS won't penetrate Shadow DOM
  • Check the CSS Parts guide for available parts

You see an SSR or hydration error

  • Web components must render client-side only
  • Use dynamic imports inside useEffect or onMounted
  • In Next.js, use the 'use client' directive. In Nuxt, use a.client.ts plugin and <ClientOnly>.

Related guides