Embed a Survey
Every SenseFolks survey is a web component. Load the package for your survey type, add the matching custom element, and pass its survey key. Choose your frontend below for the exact setup.
Choose Your Frontend Setup
Vanilla JS / HTML
Use a module script and custom element when you do not need a bundler.
<!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>The type="module" attribute is required for ES module loading. SenseFolks components target the modern browsers listed in each component reference; no classic-script fallback is shipped.
Script URLs by Survey Type
Use the package URL that matches the component you embed:
| Component | Script URL |
|---|---|
sf-fastpoll | https://unpkg.com/@sensefolks/[email protected]/dist/sf-fastpoll/sf-fastpoll.esm.js |
sf-userchoice | https://unpkg.com/@sensefolks/[email protected]/dist/sf-userchoice/sf-userchoice.esm.js |
sf-pricepoint | https://unpkg.com/@sensefolks/[email protected]/dist/sf-pricepoint/sf-pricepoint.esm.js |
sf-openfeedback | https://unpkg.com/@sensefolks/[email protected]/dist/sf-openfeedback/sf-openfeedback.esm.js |
sf-featurepriority | https://unpkg.com/@sensefolks/[email protected]/dist/sf-featurepriority/sf-featurepriority.esm.js |
sf-reaction | https://unpkg.com/@sensefolks/[email protected]/dist/sf-reaction/sf-reaction.esm.js |
React + Next.js
Load the package after the React component mounts:
// 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>
);
}For Next.js, use a client component with 'use client' and dynamic import:
// 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" />This pattern works with both the App Router and Pages Router.
TypeScript Support
Add type declarations for all SenseFolks components:
// 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
>;
}
}Create this file at types/sf-components.d.ts and include it in your tsconfig.json.
Vue + Nuxt
<!-- 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
Tell Vue to treat sf-* tags as custom elements:
// 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
Register SenseFolks in a client-only plugin and render with <ClientOnly>:
// 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
First, add the custom elements schema to your module:
// 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 {}Then create a component that loads the survey:
// 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');
}
}Use [attr.survey-key] for dynamic attribute binding in Angular templates.
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>Svelte natively supports custom elements — no additional configuration needed.
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>For Astro, use a client-side script to load the component dynamically.
Optional Embed Patterns
Conditional Loading
Load the component after a relevant interaction, such as reaching a section of the page:
// 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 embedded surveys to match your site:
/* 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-keyis the UUID from your dashboard - Verify the component is loaded before it's used (use dynamic imports)
TypeScript does not recognise the custom element
- Add the type declarations shown in the React section
- For Vue, configure
isCustomElementin 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
useEffectoronMounted - In Next.js, use the
'use client'directive. In Nuxt, use a.client.tsplugin and<ClientOnly>.