Compare commits
1 Commits
feature/1.
...
delete-res
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c866aee66 |
8
.changeset/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
||||
@@ -12,8 +12,8 @@
|
||||
// Configure properties specific to VS Code.
|
||||
"vscode": {
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": ["dbaeumer.vscode-eslint"],
|
||||
},
|
||||
"extensions": ["dbaeumer.vscode-eslint"]
|
||||
}
|
||||
},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
@@ -25,5 +25,5 @@
|
||||
"postAttachCommand": "pnpm dev --filter=web... --filter=demo...",
|
||||
|
||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "node",
|
||||
"remoteUser": "node"
|
||||
}
|
||||
|
||||
@@ -137,7 +137,3 @@ ENTERPRISE_LICENSE_KEY=
|
||||
|
||||
# set to 1 to skip onboarding for new users
|
||||
# ONBOARDING_DISABLED=1
|
||||
|
||||
# Send new users to customer.io
|
||||
# CUSTOMER_IO_API_KEY=
|
||||
# CUSTOMER_IO_SITE_ID=
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -42,6 +42,7 @@ body:
|
||||
- First time: Please read our [introductory blog post](https://formbricks.com/blog/join-the-formtribe)
|
||||
- All UI components are in the package `formbricks/ui`
|
||||
- Run `pnpm go` to find a demo app to test in-app surveys at `localhost:3002`
|
||||
- Everything is type-safe.
|
||||
- We use **chatGPT** to help refactor code.
|
||||
- Everything is type-safe
|
||||
- We use **chatGPT** to help refactor code. Use our [Formbricks ✨ megaprompt ✨](https://github.com/formbricks/formbricks/blob/main/megaprompt.md) to create the right
|
||||
context before you write your prompt.
|
||||
- Anything unclear? [Ask in Discord](https://formbricks.com/discord)
|
||||
|
||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -32,6 +32,7 @@ Fixes # (issue)
|
||||
- [ ] Removed all `console.logs`
|
||||
- [ ] Merged the latest changes from main onto my branch with `git pull origin main`
|
||||
- [ ] My changes don't cause any responsiveness issues
|
||||
- [ ] First PR at Formbricks? [Please sign the CLA!](https://formbricks.com/clmyhzfrymr4ko00hycsg1tvx) Without it we wont be able to merge it 🙏
|
||||
|
||||
### Appreciated
|
||||
|
||||
|
||||
6
.github/workflows/build-formbricks-com.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build formbricks-com
|
||||
name: Build
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
@@ -11,10 +11,10 @@ jobs:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js 20.x
|
||||
- name: Setup Node.js 18.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20.x
|
||||
node-version: 18.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
|
||||
4
.github/workflows/build-web.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build web
|
||||
name: Build
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
- name: create .env
|
||||
run: cp .env.example .env
|
||||
|
||||
- name: Generate Random ENCRYPTION_KEY
|
||||
- name: Generate Random NEXTAUTH_SECRET
|
||||
run: |
|
||||
SECRET=$(openssl rand -hex 32)
|
||||
echo "ENCRYPTION_KEY=$SECRET" >> $GITHUB_ENV
|
||||
|
||||
@@ -4,8 +4,8 @@ on:
|
||||
# "Scheduled workflows run on the latest commit on the default or base branch."
|
||||
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
|
||||
schedule:
|
||||
# This will run the job at 20:00 UTC every day of every month.
|
||||
- cron: "0 20 * * *"
|
||||
# This will run the job at 22:00 UTC every day of every month.
|
||||
- cron: "0 22 * * *"
|
||||
jobs:
|
||||
cron-reportUsageToStripe:
|
||||
env:
|
||||
|
||||
80
.github/workflows/e2e.yml
vendored
@@ -1,80 +0,0 @@
|
||||
name: E2E Tests
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
build:
|
||||
name: Run E2E Tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install Docker Compose
|
||||
run: sudo apt-get update && sudo apt-get install -y docker-compose
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install -g pnpm && pnpm install
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: pnpm exec playwright install --with-deps
|
||||
|
||||
- name: create .env
|
||||
run: cp .env.example .env
|
||||
|
||||
- name: Generate ENCRYPTION_KEY
|
||||
run: |
|
||||
SECRET=$(openssl rand -hex 32)
|
||||
echo "ENCRYPTION_KEY=$SECRET" >> $GITHUB_ENV
|
||||
|
||||
- name: Start PostgreSQL
|
||||
run: |
|
||||
cd packages/database && pnpm db:up &
|
||||
for attempt in {1..20}; do
|
||||
if nc -zv localhost 5432; then
|
||||
echo "Ready"
|
||||
break
|
||||
fi
|
||||
echo "Waiting..."
|
||||
sleep 5
|
||||
done
|
||||
pnpm db:migrate:dev
|
||||
|
||||
- name: Build App in dev mode without external dependencies
|
||||
run: |
|
||||
pnpm build:dev --filter=web...
|
||||
|
||||
- name: Serve packages for lazy loading
|
||||
run: |
|
||||
cd packages/surveys && pnpm serve &
|
||||
|
||||
- name: Run App
|
||||
run: |
|
||||
NODE_ENV=test pnpm start --filter=web &
|
||||
for attempt in {1..20}; do
|
||||
if [ $(curl -o /dev/null -s -w "%{http_code}" http://localhost:3000/health) -eq 200 ]; then
|
||||
echo "Ready"
|
||||
break
|
||||
fi
|
||||
echo "Waiting..."
|
||||
sleep 10
|
||||
done
|
||||
|
||||
- name: Test Serve endpoints
|
||||
run: |
|
||||
curl -s http://localhost:3003
|
||||
|
||||
- name: Run E2E Tests
|
||||
run: |
|
||||
pnpm test:e2e
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
137
.github/workflows/ecs-deployment.yml
vendored
@@ -1,137 +0,0 @@
|
||||
name: ECS
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch: # Add manual trigger support
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
REGISTRY: ghcr.io
|
||||
# github.repository as <account>/<repo>
|
||||
IMAGE_NAME: formbricks/formbricks-experimental
|
||||
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/formbricks?schema=public"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
# This is used to complete the identity challenge
|
||||
# with sigstore/fulcio when running outside of PRs.
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Generate Random NEXTAUTH_SECRET
|
||||
run: |
|
||||
SECRET=$(openssl rand -hex 32)
|
||||
echo "NEXTAUTH_SECRET=$SECRET" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate Random ENCRYPTION_KEY
|
||||
run: |
|
||||
SECRET=$(openssl rand -hex 32)
|
||||
echo "ENCRYPTION_KEY=$SECRET" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Depot CLI
|
||||
uses: depot/setup-action@v1
|
||||
|
||||
# https://github.com/sigstore/cosign-installer
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1
|
||||
with:
|
||||
cosign-release: "v2.1.1"
|
||||
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry
|
||||
uses: docker/login-action@v3 # v3.0.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Extract metadata (tags, labels) for Docker
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5 # v5.0.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=sha,format=long
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
# Build and push Docker image with Buildx
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: depot/build-push-action@v1
|
||||
env:
|
||||
NEXT_PUBLIC_SENTRY_DSN: ${{ secrets.NEXT_PUBLIC_SENTRY_DSN }}
|
||||
with:
|
||||
project: tw0fqmsx3c
|
||||
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
|
||||
context: .
|
||||
file: ./apps/web/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
build-args: |
|
||||
NEXTAUTH_SECRET=${{ env.NEXTAUTH_SECRET }}
|
||||
DATABASE_URL=${{ env.DATABASE_URL }}
|
||||
ENCRYPTION_KEY=${{ env.ENCRYPTION_KEY }}
|
||||
NEXT_PUBLIC_SENTRY_DSN=${{ env.NEXT_PUBLIC_SENTRY_DSN }}
|
||||
|
||||
- name: Sign the images with GitHub OIDC Token
|
||||
env:
|
||||
DIGEST: ${{ steps.build-and-push.outputs.digest }}
|
||||
TAGS: ${{ steps.meta.outputs.tags }}
|
||||
run: |
|
||||
images=""
|
||||
for tag in ${TAGS}; do
|
||||
images+="${tag}@${DIGEST} "
|
||||
done
|
||||
cosign sign --yes ${images}
|
||||
|
||||
deploy:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
|
||||
- name: Download task definition
|
||||
run: |
|
||||
aws ecs describe-task-definition --task-definition prod-webapp-ecs-service --query taskDefinition > task-definition.json
|
||||
|
||||
- name: Fill in the new image ID in the Amazon ECS task definition
|
||||
id: task-def
|
||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||
with:
|
||||
task-definition: task-definition.json
|
||||
container-name: prod-webapp-container
|
||||
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
|
||||
- name: Deploy Amazon ECS task definition
|
||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
||||
with:
|
||||
task-definition: ${{ steps.task-def.outputs.task-definition }}
|
||||
service: prod-webapp-ecs-service
|
||||
cluster: prod-core-infra-ecs-cluster
|
||||
wait-for-service-stability: true
|
||||
40
.github/workflows/playwright.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: E2E Tests
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
build:
|
||||
name: Run E2E Tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install Docker Compose
|
||||
run: sudo apt-get update && sudo apt-get install -y docker-compose
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install -g pnpm && pnpm install
|
||||
|
||||
- name: Build Formricks JS package
|
||||
run: pnpm build --filter=js
|
||||
|
||||
- name: Build Formbricks Image & Run
|
||||
run: docker-compose up -d
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: pnpm exec playwright install --with-deps
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: pnpm test:e2e
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
2
.github/workflows/pr.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
e2e-test:
|
||||
name: Run E2E Tests
|
||||
uses: ./.github/workflows/e2e.yml
|
||||
uses: ./.github/workflows/playwright.yml
|
||||
secrets: inherit
|
||||
|
||||
required:
|
||||
|
||||
20
.github/workflows/release-docker-github.yml
vendored
@@ -44,9 +44,6 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Depot CLI
|
||||
uses: depot/setup-action@v1
|
||||
|
||||
# Install the cosign tool except on PR
|
||||
# https://github.com/sigstore/cosign-installer
|
||||
- name: Install cosign
|
||||
@@ -55,6 +52,17 @@ jobs:
|
||||
with:
|
||||
cosign-release: "v2.1.1"
|
||||
|
||||
# Add support for more platforms with QEMU (optional)
|
||||
# https://github.com/docker/setup-qemu-action
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
# Set up BuildKit Docker container builder to be able to build
|
||||
# multi-platform images and export cache
|
||||
# https://github.com/docker/setup-buildx-action
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3 # v3.0.0
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
@@ -77,13 +85,11 @@ jobs:
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: depot/build-push-action@v1
|
||||
uses: docker/build-push-action@v5 # v5.0.0
|
||||
with:
|
||||
project: tw0fqmsx3c
|
||||
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
|
||||
context: .
|
||||
file: ./apps/web/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
# platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
@@ -126,6 +126,8 @@ Formbricks has a hosted cloud offering with a generous free plan to get you up a
|
||||
|
||||
Formbricks is available Open-Source under AGPLv3 license. You can host Formbricks on your own servers using Docker without a subscription.
|
||||
|
||||
(In the future we may develop additional features that aren't in the free Open-Source version).
|
||||
|
||||
If you opt for self-hosting Formbricks, here are a few options to consider:
|
||||
|
||||
#### Docker
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"dependencies": {
|
||||
"@formbricks/js": "workspace:*",
|
||||
"@heroicons/react": "^2.1.1",
|
||||
"next": "14.1.0",
|
||||
"next": "14.0.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Head, Html, Main, NextScript } from "next/document";
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en" className="h-full bg-slate-50">
|
||||
<Html lang="en" className="h-full bg-gray-50">
|
||||
<Head />
|
||||
<body className="h-full">
|
||||
<Main />
|
||||
|
||||
@@ -100,22 +100,22 @@ export default function AppPage({}) {
|
||||
</div>
|
||||
|
||||
<div className="md:grid md:grid-cols-3">
|
||||
<div className="col-span-3 rounded-lg border border-slate-300 bg-slate-100 p-6 dark:border-slate-600 dark:bg-slate-800">
|
||||
<div className="col-span-3 rounded-lg border border-slate-300 bg-slate-100 p-6 dark:border-gray-600 dark:bg-gray-800">
|
||||
<h3 className="text-lg font-semibold dark:text-white">
|
||||
Reset person / pull data from Formbricks app
|
||||
</h3>
|
||||
<p className="text-slate-700 dark:text-slate-300">
|
||||
<p className="text-slate-700 dark:text-gray-300">
|
||||
On formbricks.reset() a few things happen: <strong>New person is created</strong> and{" "}
|
||||
<strong>surveys & no-code actions are pulled from Formbricks:</strong>.
|
||||
</p>
|
||||
<button
|
||||
className="my-4 rounded-lg bg-slate-500 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
|
||||
className="my-4 rounded-lg bg-slate-500 px-6 py-3 text-white hover:bg-slate-700 dark:bg-gray-700 dark:hover:bg-gray-600"
|
||||
onClick={() => {
|
||||
formbricks.reset();
|
||||
}}>
|
||||
Reset
|
||||
</button>
|
||||
<p className="text-xs text-slate-700 dark:text-slate-300">
|
||||
<p className="text-xs text-slate-700 dark:text-gray-300">
|
||||
If you made a change in Formbricks app and it does not seem to work, hit 'Reset' and
|
||||
try again.
|
||||
</p>
|
||||
@@ -124,7 +124,7 @@ export default function AppPage({}) {
|
||||
<div className="p-6">
|
||||
<div>
|
||||
<button
|
||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
|
||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-gray-700 dark:hover:bg-gray-600"
|
||||
onClick={() => {
|
||||
formbricks.track("Code Action");
|
||||
}}>
|
||||
@@ -132,7 +132,7 @@ export default function AppPage({}) {
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-700 dark:text-slate-300">
|
||||
<p className="text-xs text-slate-700 dark:text-gray-300">
|
||||
This button sends a{" "}
|
||||
<a href="https://formbricks.com/docs/actions/code" className="underline" target="_blank">
|
||||
Code Action
|
||||
@@ -143,12 +143,12 @@ export default function AppPage({}) {
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<div>
|
||||
<button className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
|
||||
<button className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-gray-700 dark:hover:bg-gray-600">
|
||||
No-Code Action
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-700 dark:text-slate-300">
|
||||
<p className="text-xs text-slate-700 dark:text-gray-300">
|
||||
This button sends a{" "}
|
||||
<a
|
||||
href="https://formbricks.com/docs/actions/no-code"
|
||||
@@ -172,12 +172,12 @@ export default function AppPage({}) {
|
||||
onClick={() => {
|
||||
formbricks.setAttribute("Plan", "Free");
|
||||
}}
|
||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
|
||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-gray-700 dark:hover:bg-gray-600">
|
||||
Set Plan to 'Free'
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-700 dark:text-slate-300">
|
||||
<p className="text-xs text-slate-700 dark:text-gray-300">
|
||||
This button sets the{" "}
|
||||
<a
|
||||
href="https://formbricks.com/docs/attributes/custom-attributes"
|
||||
@@ -195,12 +195,12 @@ export default function AppPage({}) {
|
||||
onClick={() => {
|
||||
formbricks.setAttribute("Plan", "Paid");
|
||||
}}
|
||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
|
||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-gray-700 dark:hover:bg-gray-600">
|
||||
Set Plan to 'Paid'
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-700 dark:text-slate-300">
|
||||
<p className="text-xs text-slate-700 dark:text-gray-300">
|
||||
This button sets the{" "}
|
||||
<a
|
||||
href="https://formbricks.com/docs/attributes/custom-attributes"
|
||||
@@ -218,12 +218,12 @@ export default function AppPage({}) {
|
||||
onClick={() => {
|
||||
formbricks.setEmail("test@web.com");
|
||||
}}
|
||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
|
||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-gray-700 dark:hover:bg-gray-600">
|
||||
Set Email
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-700 dark:text-slate-300">
|
||||
<p className="text-xs text-slate-700 dark:text-gray-300">
|
||||
This button sets the{" "}
|
||||
<a
|
||||
href="https://formbricks.com/docs/attributes/identify-users"
|
||||
@@ -242,7 +242,7 @@ export default function AppPage({}) {
|
||||
onClick={() => {
|
||||
window.location.href = "/app";
|
||||
}}
|
||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
|
||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-gray-700 dark:hover:bg-gray-600">
|
||||
Deactivate User Identification
|
||||
</button>
|
||||
</div>
|
||||
@@ -252,13 +252,13 @@ export default function AppPage({}) {
|
||||
onClick={() => {
|
||||
window.location.href = "/app?userId=true";
|
||||
}}
|
||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
|
||||
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-gray-700 dark:hover:bg-gray-600">
|
||||
Activate User Identification
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<p className="text-xs text-slate-700 dark:text-slate-300">
|
||||
<p className="text-xs text-slate-700 dark:text-gray-300">
|
||||
This button activates/deactivates{" "}
|
||||
<a
|
||||
href="https://formbricks.com/docs/attributes/identify-users"
|
||||
|
||||
37
apps/formbricks-com/app/docs/actions/code/page.mdx
Normal file
@@ -0,0 +1,37 @@
|
||||
export const metadata = {
|
||||
title: "Implementing Code Actions in Formbricks | Real-time User Action Tracking",
|
||||
description:
|
||||
"Dive into the world of Formbricks' code actions. Learn how to seamlessly integrate formbricks.track() method into your codebase, enabling real-time tracking of user actions like button clicks, visiting a specific URL. Up your survey game with precise and exact triggers.",
|
||||
};
|
||||
|
||||
#### Actions
|
||||
|
||||
# Code Actions
|
||||
|
||||
Actions can also be set in the codebase to trigger surveys. Please add the code action first in the Formbricks web interface to be able to configure your surveys to use this action.
|
||||
|
||||
After that you can fire an action using `formbricks.track()`
|
||||
|
||||
<Col>
|
||||
<CodeGroup title="Track an action">
|
||||
|
||||
```javascript
|
||||
formbricks.track("Action Name");
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
Here is an example of how to fire an action when a user clicks a button:
|
||||
<Col>
|
||||
<CodeGroup title="Track Button Click">
|
||||
|
||||
```javascript
|
||||
const handleClick = () => {
|
||||
formbricks.track("Button Clicked");
|
||||
};
|
||||
|
||||
return <button onClick={handleClick}>Click Me</button>;
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
30
apps/formbricks-com/app/docs/actions/no-code/page.mdx
Normal file
@@ -0,0 +1,30 @@
|
||||
export const metadata = {
|
||||
title: "Implementing No-Code Actions in Formbricks | Real-time User Action Tracking",
|
||||
description:
|
||||
"Discover the power of Formbricks' No-Code Actions. Easily set up triggers based on Page URL, innerText, and CSS Selectors without touching a line of code. Inccrease user engagement and get insights at precise moments in the user journey.",
|
||||
};
|
||||
|
||||
#### Actions
|
||||
|
||||
# No-Code Actions
|
||||
|
||||
No-Code actions can be set up within Formbricks with just a few clicks. There are three types of No-Code actions:
|
||||
|
||||
## Page URL Action
|
||||
|
||||
The page URL action is triggered, when a user visits a specific page in your application. There are several match conditions:
|
||||
|
||||
- `exactMatch`: The URL should exactly match the provided string.
|
||||
- `contains`: The URL should contain the specified string as a substring.
|
||||
- `startsWith`: The URL should start with the specified string.
|
||||
- `endsWith`: The URL should end with the specified string.
|
||||
- `notMatch`: The URL should not match the specified condition.
|
||||
- `notContains`: The URL should not contain the specified string as a substring.
|
||||
|
||||
## innerText Action
|
||||
|
||||
The innerText action checks if the `innerText` of a clicked HTML element matches a specific text, e.g. the label of a button. Display a survey on any button click!
|
||||
|
||||
## CSS Selector Action
|
||||
|
||||
The CSS Selector action checks if the provided CSS selector matches the selector of a clicked HTML element. The CSS selector can be a class, id or any other CSS selector within your website. Display a survey on any element click!
|
||||
23
apps/formbricks-com/app/docs/actions/why/page.mdx
Normal file
@@ -0,0 +1,23 @@
|
||||
export const metadata = {
|
||||
title: "Using Actions in Formbricks | Fine-tuning User Moments",
|
||||
description:
|
||||
"Dive deep into how actions in Formbricks help products and teams to engage users at precise moments in their journey. Discover the power of actions, from coding to no-code setups, to refine user targeting and generate richer, more detailed user insights.",
|
||||
};
|
||||
|
||||
#### Actions
|
||||
|
||||
# What are actions and why are they useful?
|
||||
|
||||
You want to understand what your users think and feel during specific moments in the user journey. To be able to ask at exactly the right point in time, you need actions.
|
||||
|
||||
## What are actions?
|
||||
|
||||
Actions are a little notification sent from your application to Formbricks. You decide which actions are sent either in your [Code](/docs/actions/code) or by setting up a [No-Code](/docs/actions/no-code) action within Formbricks.
|
||||
|
||||
## How do actions work?
|
||||
|
||||
When a predefined action happens in your app, the Formbricks widget notices. This action can then trigger a survey to be shown to the user and is stored in the database.
|
||||
|
||||
## Why are actions useful?
|
||||
|
||||
Actions help you to display your surveys at the right time. Later on, you will be able to segment your users based on the actions they have triggered in the past. This way, you can create much more granular user segments, e.g. only target users that already have used a specific feature.
|
||||
@@ -0,0 +1,57 @@
|
||||
export const metadata = {
|
||||
title: "Guide for Setting Custom Attributes | Formbricks Documentation",
|
||||
description:
|
||||
"Learn how to set attributes in code using setAttribute function. Enhance user segmentation, target surveys effectively, and gather valuable insights for better decisions. Easily send user-specific details for better survey segmentation and gain deeper insights.",
|
||||
};
|
||||
|
||||
#### Attributes
|
||||
|
||||
# Setting attributes with code
|
||||
|
||||
One way to send attributes to Formbricks is in your code. In Formbricks, there are two special attributes for [user identification](/docs/attributes/identify-users)(user ID & email) and custom attributes. An example:
|
||||
|
||||
## Setting during Initialization
|
||||
|
||||
It's recommended to set custom user attributes directly during the initialization of Formbricks for better user identification.
|
||||
|
||||
<Col>
|
||||
<CodeGroup title="Set custom attributes during initialization">
|
||||
|
||||
```javascript
|
||||
formbricks.init({
|
||||
environmentId: "<environment-id>",
|
||||
apiHost: "<api-host>",
|
||||
userId: "<user_id>",
|
||||
attributes: {
|
||||
plan: "free",
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
## Setting independently
|
||||
|
||||
You can use the setAttribute function to set any custom attribute for the user (e.g. name, plan, etc.) anywhere in the user journey. Formbricks maintains a state of the current user inside the browser and makes sure attributes aren't sent to the backend twice.
|
||||
|
||||
<Col>
|
||||
<CodeGroup title="Setting Plan to Pro">
|
||||
|
||||
```javascript
|
||||
formbricks.setAttribute("Plan", "Pro");
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
Generally speaking, the setAttribute function works like this:
|
||||
<Col>
|
||||
<CodeGroup title="Setting Custom Attributes">
|
||||
|
||||
```javascript
|
||||
formbricks.setAttribute("attribute_key", "attribute_value");
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
Where `attributeName` is the name of the attribute you want to set, and `attributeValue` is the value of the attribute you want to set.
|
||||
@@ -1,28 +1,18 @@
|
||||
export const metadata = {
|
||||
title: "Understanding User Attributes in Formbricks Surveys",
|
||||
title: "User Identification in Formbricks | Enhancing Survey Feedback",
|
||||
description:
|
||||
"Dive into the importance of attributes in surveys. Learn how key-value pairs can significantly improve survey targeting, enhance feedback quality, and guide data-driven decisions with Formbricks.",
|
||||
"A comprehensive guide on identifying users in Formbricks without compromising privacy. Learn how to set User ID, email, and custom attributes to optimize survey targeting, recontact users, and control survey intervals, all while respecting user anonymity.",
|
||||
};
|
||||
|
||||
#### In-App Surveys
|
||||
#### Attributes
|
||||
|
||||
# Attributes
|
||||
# Identifying Users
|
||||
|
||||
Surveying your user base without segmentation leads to weak results and survey fatigue. Attributes help you segment your users into groups.
|
||||
At Formbricks, we value user privacy. By default, Formbricks doesn't collect or store any personal information from your users. However, we understand that it can be helpful for you to know which user submitted the feedback and also functionality like recontacting users and controlling the waiting period between surveys requires identifying the users. That's why we provide a way for you to share existing user data from your app, so you can view it in our dashboard.
|
||||
|
||||
## How do Attributes work?
|
||||
If you would like to use the User Identification feature of Formbricks, target surveys to specific user segments and see more information about the user who responded to a survey, you can identify users by setting a User ID, email, and custom attributes. This guide will walk you through how to do that.
|
||||
|
||||
Attributes are **key-value pairs** that you can set for each person individually.
|
||||
Attributes are sent from your application to Formbricks and are associated with the current user. We store it in our database and allow you to use it the next time you create a survey. They help show surveys to the right group of people.
|
||||
|
||||
<Note>
|
||||
At Formbricks, we value user privacy. By default, Formbricks doesn't collect or store any personal information from your users.
|
||||
</Note>
|
||||
|
||||
## Identifying Users
|
||||
To use the User Identification feature of Formbricks, target surveys to specific user segments and see more information about the user who responded to a survey, you can identify users by setting a User ID, email, and custom attributes. Below is how to do that.
|
||||
|
||||
### Setting User ID
|
||||
## Setting User ID
|
||||
|
||||
To enable the User identification feature you need to set the `userId` in the init() call of Formbricks. Only when the `userId` is set the person will be visible in the Formbricks dashboard. The `userId` can be any string and it's best to use the default identifier you use in your app (e.g. unique id from database or the email address if it's unique) but you can also anonymize these as long as they are unique for every user.
|
||||
|
||||
@@ -40,7 +30,7 @@ formbricks.init({
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
### Enhanced Initialization with User Attributes
|
||||
## Enhanced Initialization with User Attributes
|
||||
|
||||
In addition to setting the `userId`, Formbricks allows you to set user attributes right at the initialization. This ensures that your user data is seamlessly integrated from the start. Here's how you can include user attributes in the `init()` function:
|
||||
|
||||
@@ -62,7 +52,7 @@ formbricks.init({
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
### Setting User Email
|
||||
## Setting User Email
|
||||
|
||||
The `userId` is the main identifier used in Formbricks and user identification is only enabled when it is set. In addition to the userId you can also set attributes that describes the user better. The email address can be set using the setEmail function:
|
||||
|
||||
@@ -75,8 +65,7 @@ formbricks.setEmail("user@example.com");
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
## Setting Custom User Attributes
|
||||
### Setting Custom User Attributes
|
||||
|
||||
You can use the setAttribute function to set any custom attribute for the user (e.g. name, plan, etc.):
|
||||
|
||||
@@ -89,20 +78,6 @@ formbricks.setAttribute("Plan", "free");
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
Generally speaking, the setAttribute function works like this:
|
||||
<Col>
|
||||
<CodeGroup title="Setting Custom Attributes">
|
||||
|
||||
```javascript
|
||||
formbricks.setAttribute("attribute_key", "attribute_value");
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
Where `attributeName` is the name of the attribute you want to set, and `attributeValue` is the value of the attribute you want to set.
|
||||
|
||||
|
||||
### Logging Out Users
|
||||
|
||||
When a user logs out of your webpage, make sure to log them out of Formbricks as well. This will prevent new activity from being associated with an incorrect user. Use the logout function:
|
||||
23
apps/formbricks-com/app/docs/attributes/why/page.mdx
Normal file
@@ -0,0 +1,23 @@
|
||||
export const metadata = {
|
||||
title: "Understanding User Attributes in Formbricks Surveys",
|
||||
description:
|
||||
"Dive into the importance of attributes in surveys. Learn how key-value pairs can significantly improve survey targeting, enhance feedback quality, and guide data-driven decisions with Formbricks.",
|
||||
};
|
||||
|
||||
#### Attributes
|
||||
|
||||
# What are attributes and why are they useful?
|
||||
|
||||
Surveying your user base without segmentation leads to weak results and survey fatigue. Attributes help you segment your users into groups.
|
||||
|
||||
## What are attributes?
|
||||
|
||||
Attributes are key-value pairs that you can set for each person individually. For example, the attribute "Plan" can be set to "Free" or "Paid".
|
||||
|
||||
## How do attributes work?
|
||||
|
||||
Attributes are sent from your application to Formbricks and are associated with the current user. We store it in our database and allow you to use it the next time you create a survey.
|
||||
|
||||
## Why are attributes useful?
|
||||
|
||||
Attributes help show surveys to the right group of people. For example, you can show a survey to all users who have a "Plan" attribute set to "Paid".
|
||||
@@ -46,8 +46,7 @@ To run the Churn Survey in your app you want to proceed as follows:
|
||||
4. Prevent that churn!
|
||||
|
||||
<Note>
|
||||
## Formbricks Widget running?
|
||||
We assume that you have already installed the Formbricks Widget in your web
|
||||
## Formbricks Widget running? We assume that you have already installed the Formbricks Widget in your web
|
||||
app. It’s required to display messages and surveys in your app. If not, please follow the [Quick Start Guide
|
||||
(takes 15mins max.)](/docs/getting-started/quickstart-in-app-survey)
|
||||
</Note>
|
||||
@@ -120,8 +119,7 @@ Whenever a user visits this page, matches the filter conditions above and the re
|
||||
Here is our complete [Actions manual](/docs/actions/why) covering [Code](/docs/actions/code) and [No-Code](/docs/actions/no-code) Actions.
|
||||
|
||||
<Note>
|
||||
## Pre-churn flow coming soon
|
||||
We’re currently building full-screen survey pop-ups. You’ll be able to prevent
|
||||
## Pre-churn flow coming soon We’re currently building full-screen survey pop-ups. You’ll be able to prevent
|
||||
users from closing the survey unless they respond to it. It’s certainly debatable if you want that but you
|
||||
could force them to click through the survey before letting them cancel 🤷
|
||||
</Note>
|
||||
@@ -158,8 +156,7 @@ These settings make sure the survey is always displayed, when a user wants to Ca
|
||||
/>
|
||||
|
||||
<Note>
|
||||
## Formbricks Widget running?
|
||||
You need to have the Formbricks Widget installed to display the Churn Survey
|
||||
## Formbricks Widget running? You need to have the Formbricks Widget installed to display the Churn Survey
|
||||
in your app. Please follow [this tutorial (Step 4 onwards)](/docs/getting-started/quickstart-in-app-survey)
|
||||
to install the widget.
|
||||
</Note>
|
||||
|
||||
@@ -43,8 +43,7 @@ To run the Feature Chaser survey in your app you want to proceed as follows:
|
||||
2. Setup a user action to display survey at the right point in time
|
||||
|
||||
<Note>
|
||||
## Formbricks Widget running?
|
||||
We assume that you have already installed the Formbricks Widget in your web
|
||||
## Formbricks Widget running? We assume that you have already installed the Formbricks Widget in your web
|
||||
app. It’s required to display messages and surveys in your app. If not, please follow the [Quick Start Guide
|
||||
(takes 15mins max.)](/docs/getting-started/quickstart-in-app-survey)
|
||||
</Note>
|
||||
@@ -128,8 +127,7 @@ Lastly, scroll down to “Recontact Options”. Here you have full freedom to de
|
||||
<Image src={Publish} alt="Publish survey" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
|
||||
|
||||
<Note>
|
||||
## Formbricks Widget running?
|
||||
You need to have the Formbricks Widget installed to display the Feature Chaser
|
||||
## Formbricks Widget running? You need to have the Formbricks Widget installed to display the Feature Chaser
|
||||
in your app. Please follow [this tutorial (Step 4 onwards)](/docs/getting-started/quickstart-in-app-survey)
|
||||
to install the widget.
|
||||
</Note>
|
||||
|
||||
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 45 KiB |
@@ -1,113 +0,0 @@
|
||||
import DemoPreview from "@/components/dummyUI/DemoPreview";
|
||||
import Image from "next/image";
|
||||
import NewsletterSurveyType from './choose-survey-type.webp';
|
||||
import NewsletterSurveyEmbedCode from './embed-survey-code-in-your-email.webp';
|
||||
import NewsletterSurveyEmbedPrompt from './embed-survey-prompt.webp';
|
||||
import NewsletterSurveyEditor from './improve-newsletter-content-editor-formbricks.webp';
|
||||
import NewsletterSurvey from './improve-newsletter-content-survey-location.webp';
|
||||
export const metadata = {
|
||||
title: "Measure email content quality with Formbricks",
|
||||
description:
|
||||
"Measuring the content quality of both transactional and marketing email is a key element for improving customer communication.",
|
||||
};
|
||||
|
||||
#### Best Practices
|
||||
|
||||
# Improve Email Content
|
||||
Email remains the predominant way to communicate with your customers. Measure the effectiveness to improve your offering.
|
||||
|
||||
|
||||
## Purpose
|
||||
|
||||
Measuring the content quality of both transactional and marketing email is a key element for improving customer communication.
|
||||
|
||||
## Preview
|
||||
|
||||
<DemoPreview template="Improve Newsletter Content" />
|
||||
|
||||
## Formbricks Approach
|
||||
|
||||
- Embed the survey into your email so it’s part of the newsletter.
|
||||
- Use link prefilling to store the answer users clicked on in the email.
|
||||
- Dynamic user identification to append reader's email for personalized profiles and follow ups.
|
||||
|
||||
## Installation
|
||||
|
||||
To embed the newsletter survey into your email, follow these steps:
|
||||
|
||||
1. Create new 'Improve Newsletter Content' survey at [app.formbricks.com](https://app.formbricks.com/)
|
||||
2. Select how you where you want to display the survey.
|
||||
3. Copy the embed code anywhere you want in your newsletter.
|
||||
|
||||
### 1. Create new 'Improve Newsletter Content' Survey
|
||||
|
||||
If you don't have an account yet, create one at [app.formbricks.com](https://app.formbricks.com/auth/signup)
|
||||
|
||||
Then, create a new survey and look for the "Improve Newsletter Content" template:
|
||||
|
||||
<Image
|
||||
src={NewsletterSurvey}
|
||||
alt="Create Improve Newsletter Content by template"
|
||||
quality="100"
|
||||
className="rounded-lg max-w-full sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
### 2. Customize Survey questions
|
||||
|
||||
Customize survey questions, emojis or stars however you like:
|
||||
|
||||
<Image
|
||||
src={NewsletterSurveyEditor}
|
||||
alt="Edit Improve Newsletter Content template"
|
||||
quality="100"
|
||||
className="rounded-lg max-w-full sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
### 3. Configure Survey Settings
|
||||
|
||||
When you are done customizing your survey questions, navigate to the Settings tab and choose the type of survey you want. You need to choose Link Survey:
|
||||
|
||||
<Image
|
||||
src={NewsletterSurveyType}
|
||||
alt="Choose survey type"
|
||||
quality="100"
|
||||
className="rounded-lg max-w-full sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
### 4. Choose how you want to embed your survey
|
||||
|
||||
After publishing your survey, a modal that prompts you to embed your survey will pop up.
|
||||
|
||||
<Image
|
||||
src={NewsletterSurveyEmbedPrompt}
|
||||
alt="Embed newsletter survey"
|
||||
quality="100"
|
||||
className="rounded-lg max-w-full sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
Select the Embed Survey card and you will be directed to another modal, where the first embed option displayed will be to embed the survey in an email.
|
||||
|
||||
### 5. Copy code to embed the survey in your newsletter
|
||||
|
||||
Click the button with the “View Embed Code” text at the top right corner of the modal and simply paste the HTML code for your survey anywhere you want it in your newsletter. You can see the preview in the below image:
|
||||
|
||||
<Image
|
||||
src={NewsletterSurveyEmbedCode}
|
||||
alt="Embed survey code"
|
||||
quality="100"
|
||||
className="rounded-lg max-w-full sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
And you're done! Send a test email to yourself and try it out 🤓
|
||||
|
||||
|
||||
## Learn about data prefilling
|
||||
|
||||
<Note>
|
||||
## How does data prefilling work?
|
||||
Learn about how link prefilling and user identification maximize your insights in [this detailed guide](/blog/how-smart-writers-use-formbricks-open-source-tool-to-measure-the-quality-of-their-newsletter-content).
|
||||
</Note>
|
||||
|
||||
###
|
||||
|
||||
# That’s it! 🎉
|
||||
@@ -43,8 +43,7 @@ To display the Trial Conversion Survey in your app you want to proceed as follow
|
||||
3. Print that 💸
|
||||
|
||||
<Note>
|
||||
## Formbricks Widget running?
|
||||
We assume that you have already installed the Formbricks Widget in your web
|
||||
## Formbricks Widget running? We assume that you have already installed the Formbricks Widget in your web
|
||||
app. It’s required to display messages and surveys in your app. If not, please follow the [Quick Start Guide
|
||||
(takes 15mins max.)](/docs/getting-started/quickstart-in-app-survey)
|
||||
</Note>
|
||||
@@ -80,8 +79,7 @@ Save, and move over to the “Audience” tab.
|
||||
### 3. Pre-segment your audience (coming soon)
|
||||
|
||||
<Note>
|
||||
## Filter by attribute coming soon
|
||||
We're working on pre-segmenting users by attributes. We will update this
|
||||
## Filter by attribute coming soon We're working on pre-segmenting users by attributes. We will update this
|
||||
manual in the next days.
|
||||
</Note>
|
||||
|
||||
@@ -138,8 +136,7 @@ Lastly, scroll down to “Recontact Options”. Here you have to choose the corr
|
||||
<Image src={Publish} alt="Publish survey" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
|
||||
|
||||
<Note>
|
||||
## Formbricks Widget running?
|
||||
You need to have the Formbricks Widget installed to display the Feedback Box
|
||||
## Formbricks Widget running? You need to have the Formbricks Widget installed to display the Feedback Box
|
||||
in your app. Please follow [this tutorial (Step 4 onwards)](/docs/getting-started/quickstart-in-app-survey)
|
||||
to install the widget.
|
||||
</Note>
|
||||
|
||||
@@ -48,8 +48,7 @@ To display an Interview Prompt in your app you want to proceed as follows:
|
||||
3. That’s it! 🎉
|
||||
|
||||
<Note>
|
||||
## Formbricks Widget running?
|
||||
We assume that you have already installed the Formbricks Widget in your web
|
||||
## Formbricks Widget running? We assume that you have already installed the Formbricks Widget in your web
|
||||
app. It’s required to display messages and surveys in your app. If not, please follow the [Quick Start Guide
|
||||
(15mins).](/docs/getting-started/quickstart-in-app-survey)
|
||||
</Note>
|
||||
@@ -92,8 +91,7 @@ Save, and move over to the “Audience” tab.
|
||||
### 3. Pre-segment your audience (coming soon)
|
||||
|
||||
<Note>
|
||||
## Filter by attribute coming soon
|
||||
We're working on pre-segmenting users by attributes. We will update this
|
||||
## Filter by attribute coming soon We're working on pre-segmenting users by attributes. We will update this
|
||||
manual in the next few days.
|
||||
</Note>
|
||||
|
||||
@@ -110,8 +108,7 @@ To create the trigger to show your Interview Prompt, go to the “Audience” ta
|
||||
<Image src={AddAction} alt="Add action" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
|
||||
|
||||
<Note>
|
||||
## You can also add actions in your code
|
||||
You can also create [Code Actions](/docs/actions/code) using
|
||||
## You can also add actions in your code You can also create [Code Actions](/docs/actions/code) using
|
||||
`formbricks.track("Eventname")` - they will automatically appear in your Actions overview as long as the SDK
|
||||
is embedded.
|
||||
</Note>
|
||||
@@ -164,8 +161,7 @@ Scroll down to “Recontact Options”. Here you have to choose the correct sett
|
||||
<Image src={Publish} alt="Publish survey" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
|
||||
|
||||
<Note>
|
||||
## Formbricks Widget running?
|
||||
You need to have the Formbricks Widget installed to display the Feedback Box
|
||||
## Formbricks Widget running? You need to have the Formbricks Widget installed to display the Feedback Box
|
||||
in your app. Please follow [this tutorial (Step 4 onwards)](/docs/getting-started/quickstart-in-app-survey)
|
||||
to install the widget.
|
||||
</Note>
|
||||
|
||||
@@ -42,8 +42,7 @@ To display the Product-Market Fit survey in your app you want to proceed as foll
|
||||
3. Setup the user action to display survey at good point in time
|
||||
|
||||
<Note>
|
||||
## Formbricks Widget running?
|
||||
We assume that you have already installed the Formbricks Widget in your web
|
||||
## Formbricks Widget running? We assume that you have already installed the Formbricks Widget in your web
|
||||
app. It’s required to display messages and surveys in your app. If not, please follow the [Quick Start Guide
|
||||
(15mins).](/docs/getting-started/quickstart-in-app-survey)
|
||||
</Note>
|
||||
@@ -79,8 +78,7 @@ Save, and move over to where the magic happens: The “Audience” tab.
|
||||
### 3. Pre-segment your audience (coming soon)
|
||||
|
||||
<Note>
|
||||
## Filter by attribute coming soon
|
||||
We're working on pre-segmenting users by attributes. We will update this
|
||||
## Filter by attribute coming soon We're working on pre-segmenting users by attributes. We will update this
|
||||
manual in the next days.
|
||||
</Note>
|
||||
|
||||
@@ -141,8 +139,7 @@ Lastly, scroll down to “Recontact Options”. Here you have to choose the corr
|
||||
<Image src={Publish} alt="Publish survey" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
|
||||
|
||||
<Note>
|
||||
## Formbricks Widget running?
|
||||
You need to have the Formbricks Widget installed to display the Feedback Box
|
||||
## Formbricks Widget running? You need to have the Formbricks Widget installed to display the Feedback Box
|
||||
in your app. Please follow [this tutorial (Step 4 onwards)](/docs/getting-started/quickstart-in-app-survey)
|
||||
to install the widget.
|
||||
</Note>
|
||||
|
||||
@@ -42,4 +42,6 @@ If you are at all unsure, just raise it as an enhancement issue first and tell u
|
||||
|
||||
To be able to keep working on Formbricks over the coming years, we need to collect a CLA from all relevant contributors.
|
||||
|
||||
Once you open a PR, you will get a message from the CLA bot to fill out the form. Please note that we can only get your contribution merged when we have a CLA signed by you.
|
||||
Please note that we can only get your contribution merged when we have a CLA signed by you.
|
||||
|
||||
To access the CLA form, please click [here](https://formbricks.com/clmyhzfrymr4ko00hycsg1tvx)
|
||||
|
||||
@@ -63,7 +63,7 @@ This will install the Formbricks codebase and all the dependencies on your local
|
||||
1. Wait for the web and demo apps to launch on Gitpod. This automatically opens the `apps/demo/.env` file. Utilize dynamic localhost URLs (e.g., `localhost:3000` for signup and `localhost:8025` for email confirmation) to configure `NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID`. After creating your account and finding the `ID` in the URL at `localhost:3000`, replace `YOUR_ENVIRONMENT_ID` in the `.env` file located in `app/demo`.
|
||||
|
||||
**Web Component Initialization:**
|
||||
- We initialize the @formbricks/web component during prebuilds. This involves:
|
||||
- we initialize the @formbricks/web component during prebuilds. This involves:
|
||||
1. Installing build dependencies for the `@formbricks/web#go` task from turbo.json in prebuilds to save time.
|
||||
2. Starting PostgreSQL and Mailhog containers for running migrations in prebuilds.
|
||||
3. To prevent the "Init" task from running indefinitely due to prebuild rules, a cleanup `docker compose down` step i.e. `db:down` is added to `turbo.json`. This step is designed to halt the execution of containers that are currently running.
|
||||
@@ -93,8 +93,8 @@ After clicking the one-click setup button, Gitpod will open a new tab or window.
|
||||
|
||||
### 2. Authorizing in Gitpod
|
||||
|
||||
<Image src={GitpodAuth} alt="Gitpod Auth Page" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
|
||||
- This is the Gitpod Authentication Page. It appears when you click the "Open in GitPod" button and Gitpod needs
|
||||
<Image src={GitpodAuth} alt="Gitpod Auth Page" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />-
|
||||
This is the Gitpod Authentication Page. It appears when you click the "Open in GitPod" button and Gitpod needs
|
||||
to authenticate your access to the workspace. Click on 'Continue With Github' to authorize your GitPod session.
|
||||
|
||||
### 3. Creating a New Workspace
|
||||
@@ -159,7 +159,7 @@ Here are the ports and corresponding URLs for the services within your Gitpod en
|
||||
|
||||
- **Port 1025**:
|
||||
|
||||
- **Service**: SMTP server
|
||||
- **Service**: SMPT server
|
||||
- **Description**: SMTP Server for sending and receiving email messages. This server is responsible for handling email communication.
|
||||
|
||||
- **Port 8025**:
|
||||
@@ -304,7 +304,7 @@ cp .env.example .env
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
4. Generate & set some secret values mandatory for the `ENCRYPTION_KEY` & `NEXTAUTH_SECRET` in the .env file. You can use the following command to generate the random string of required length:
|
||||
4. Generate & set some secret values mandatory for the ENCRYPTION_KEY & NEXTAUTH_SECRET in the .env file. You can use the following command to generate the random string of required length:
|
||||
|
||||
<Col>
|
||||
<CodeGroup title="Set value of ENCRYPTION_KEY">
|
||||
@@ -330,8 +330,8 @@ pnpm go
|
||||
</Col>
|
||||
This starts the Formbricks main app (plus all its dependencies) as well as the following services using Docker:
|
||||
|
||||
- A `postgres` container for hosting your database,
|
||||
- A `mailhog` container that acts as a mock SMTP server and shows received mails in a web UI (forwarded to your host's `localhost:8025`)
|
||||
- a `postgres` container for hosting your database,
|
||||
- a `mailhog` container that acts as a mock SMTP server and shows received mails in a web UI (forwarded to your host's `localhost:8025`)
|
||||
- Demo App at [http://localhost:3002](http://localhost:3002)
|
||||
- Landing Page at [http://localhost:3001](http://localhost:3001)
|
||||
|
||||
@@ -361,4 +361,4 @@ pnpm build
|
||||
|
||||
---
|
||||
|
||||
Can’t figure it out? Join our [Discord](https://discord.com/invite/3YFcABF2Ts)!
|
||||
Still can’t figure it out? Join our [Discord](https://discord.com/invite/3YFcABF2Ts)!
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
export const metadata = {
|
||||
title: "Using Actions in Formbricks | Fine-tuning User Moments",
|
||||
description:
|
||||
"Dive deep into how actions in Formbricks help products and teams to engage users at precise moments in their journey. Discover the power of actions, from coding to no-code setups, to refine user targeting and generate richer, more detailed user insights.",
|
||||
};
|
||||
|
||||
#### In-App Surveys
|
||||
|
||||
# Actions
|
||||
|
||||
You want to understand what your users think and feel during specific moments in the user journey. To be able to ask at exactly the right point in time, you need actions.
|
||||
|
||||
## How do Actions work?
|
||||
|
||||
Actions are a little notification sent from your application to Formbricks. When a predefined action happens in your app, the Formbricks widget notices. This action can then trigger a survey to be shown to the user and is stored in the database.
|
||||
You decide which actions are sent either in your [Code](#code-actions) or by setting up a [No-Code](#no-code-actions) action within Formbricks.
|
||||
|
||||
### Why are actions useful?
|
||||
|
||||
Actions help you to display your surveys at the right time. Later on, you will be able to segment your users based on the actions they have triggered in the past. This way, you can create much more granular user segments, e.g. only target users that already have used a specific feature.
|
||||
|
||||
## No-Code Actions
|
||||
|
||||
No-Code actions can be set up within Formbricks with just a few clicks. There are three types of No-Code actions:
|
||||
|
||||
1. **Page URL Action**
|
||||
|
||||
The page URL action is triggered, when a user visits a specific page in your application. There are several match conditions:
|
||||
|
||||
- `exactMatch`: The URL should exactly match the provided string.
|
||||
- `contains`: The URL should contain the specified string as a substring.
|
||||
- `startsWith`: The URL should start with the specified string.
|
||||
- `endsWith`: The URL should end with the specified string.
|
||||
- `notMatch`: The URL should not match the specified condition.
|
||||
- `notContains`: The URL should not contain the specified string as a substring.
|
||||
|
||||
2. **innerText Action**
|
||||
|
||||
The innerText action checks if the `innerText` of a clicked HTML element matches a specific text, e.g. the label of a button. Display a survey on any button click!
|
||||
|
||||
3. **CSS Selector Action**
|
||||
|
||||
The CSS Selector action checks if the provided CSS selector matches the selector of a clicked HTML element. The CSS selector can be a class, id or any other CSS selector within your website. Display a survey on any element click!
|
||||
|
||||
## Code Actions
|
||||
|
||||
Actions can also be set in the codebase to trigger surveys. Please add the code action first in the Formbricks web interface to be able to configure your surveys to use this action.
|
||||
|
||||
After that you can fire an action using `formbricks.track()`
|
||||
|
||||
<Col>
|
||||
<CodeGroup title="Track an action">
|
||||
|
||||
```javascript
|
||||
formbricks.track("Action Name");
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
Here is an example of how to fire an action when a user clicks a button:
|
||||
<Col>
|
||||
<CodeGroup title="Track Button Click">
|
||||
|
||||
```javascript
|
||||
const handleClick = () => {
|
||||
formbricks.track("Button Clicked");
|
||||
};
|
||||
|
||||
return <button onClick={handleClick}>Click Me</button>;
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
@@ -1,86 +0,0 @@
|
||||
export const metadata = {
|
||||
title: "Identifying Users in Formbricks In-App Surveys",
|
||||
description:
|
||||
"Dive into the importance of user identification in surveys. Boost your survey response rates and target the right users with Formbricks.",
|
||||
};
|
||||
|
||||
#### In-App Surveys
|
||||
|
||||
# User Identification
|
||||
|
||||
User Identification helps you to not only segment your users but also to see more information about the user who responded to a survey. This helps you to target surveys to specific user segments and see more information about the user who responded to a survey.
|
||||
|
||||
### Understanding Identified vs Unidentified Users
|
||||
|
||||
In Formbricks, understanding the distinction between identified and unidentified users is crucial for effective survey segmentation and targeted feedback collection.
|
||||
|
||||
| Feature | Unidentified Users | Identified Users |
|
||||
| -------------------------------------------------------- | ------------------ | ---------------- |
|
||||
| Show surveys based on **trigger** actions | ✅ | ✅ |
|
||||
| Set **recontact details** to avoid survey fatique | ✅ | ✅ |
|
||||
| Target a subset of users using **attributes & segments** | ❌ | ✅ |
|
||||
| Collect **user information** in Formbricks | ❌ | ✅ |
|
||||
| Track **custom attributes** | ❌ | ✅ |
|
||||
| Counts towards your **monthly tacked user (MTU)** limit | ❌ | ✅ |
|
||||
| Recommended for **public-facing websites** | ✅ | ❌ |
|
||||
| Recommended for **web apps after login** | ❌ | ✅ |
|
||||
|
||||
## Unidentified Users
|
||||
|
||||
Unidentified users are those without a specific userId set. Surveys can still be presented to these users based on trigger actions, but without the granularity of user-specific targeting.
|
||||
|
||||
This method is recommended for public-facing websites where users are not required to log in.
|
||||
|
||||
<Col>
|
||||
<CodeGroup title="Initialization without a user ID">
|
||||
|
||||
```javascript
|
||||
formbricks.init({
|
||||
environmentId: "<environment-id>",
|
||||
apiHost: "<api-host>",
|
||||
});
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
## Identified Users
|
||||
|
||||
Identified users are those for whom specific information has been set, notably the userId. This identification allows for more precise targeting of surveys and a deeper understanding of the feedback provided. When enabled, all information specified by you and all actions are sent to Formbricks.
|
||||
|
||||
This method is recommended for applications where users are required to log in and will often return.
|
||||
|
||||
**Setting the User ID:**
|
||||
To identify a user, set the `userId` in the Formbricks initialization call. The userId should be a unique string, such as a database ID or an email address. This is essential for the user to be visible on the Formbricks dashboard.
|
||||
|
||||
<Col>
|
||||
<CodeGroup title="Initialization with a user ID">
|
||||
|
||||
```javascript
|
||||
formbricks.init({
|
||||
environmentId: "<environment-id>",
|
||||
apiHost: "<api-host>",
|
||||
userId: "<user_id>",
|
||||
});
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
**Logging Out Users:**
|
||||
When a user logs out of your application, ensure they are also logged out of Formbricks. This prevents the association of their activities with the wrong user profile.
|
||||
|
||||
<Col>
|
||||
<CodeGroup title="Logging out User">
|
||||
|
||||
```javascript
|
||||
formbricks.logout();
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
<Note>
|
||||
To set other custom attributes for a user, view our <a href="/docs/in-app-surveys/attributes">Attributes</a>{" "}
|
||||
documentation.
|
||||
</Note>
|
||||
@@ -123,7 +123,6 @@ We will first create a Google Cloud Project and then enable the Google Sheets AP
|
||||
- `GOOGLE_SHEETS_CLIENT_ID` - Client ID
|
||||
- `GOOGLE_SHEETS_CLIENT_SECRET` - Client Secret
|
||||
16. Also use the **same Authorized redirect URI** in the `GOOGLE_SHEETS_REDIRECT_URL` environment variable.
|
||||
17. One last that we need to do is to **enable the Google Drive API** for the project. For that, go to the "**APIs & Services**" section and click on the "**Enable APIs and Services**" button and search for "**Google Drive API**" and enable it.
|
||||
|
||||
### By now, your environment variables should include the below ones as well:
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 49 KiB |
@@ -1,88 +0,0 @@
|
||||
import Image from "next/image";
|
||||
|
||||
import HomePage from "./home-page.webp";
|
||||
import SurveyQuestions from "./survey-questions.webp";
|
||||
import SurveySettings from "./survey-settings.webp";
|
||||
import SurveyResponseOptions from "./survey-response-options.webp";
|
||||
import SurveyPublished from "./survey-published.webp";
|
||||
|
||||
export const metadata = {
|
||||
title: "Formbricks Quickstart Guide: Link Surveys Made Easier & Faster",
|
||||
description:
|
||||
"Formbricks is the easiest way to create and manage link surveys. This quickstart guide will show you how to create your first link survey in under 5 minutes.",
|
||||
};
|
||||
|
||||
#### Getting Started
|
||||
|
||||
# Quickstart
|
||||
|
||||
Link Surveys make it easy for your users to give you feedback. They are a great way to get feedback from your users, without interrupting their workflow. This quickstart guide will show you how to create your first link survey in under 5 minutes.
|
||||
|
||||
## Create a free Formbricks Cloud account
|
||||
|
||||
While you can [self-host](/docs/self-hosting/deployment) Formbricks, but the quickest and easiest way to get started is with the free Cloud plan. Just [sign up here](https://app.formbricks.com/auth/signup) and click through the onboarding, until you’re here:
|
||||
|
||||
<Image
|
||||
src={HomePage}
|
||||
alt="Choose a link survey template"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl "
|
||||
/>
|
||||
|
||||
Choose one of the pre-created templates to get started. We’ll choose the **Product Market Fit** template for this quickstart guide.
|
||||
|
||||
## Create your first survey
|
||||
|
||||
On clicking the template, you’ll be forwarded to the survey editor. Here you can edit the survey questions and settings. For the sake of simplicity, we'll keep the questions as they are and move to the survey settings.
|
||||
|
||||
<Image
|
||||
src={SurveyQuestions}
|
||||
alt="Survey Editor opens up in the Formbricks App"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
Click on the **Settings** tab to edit the survey settings.
|
||||
|
||||
## Configure your survey settings
|
||||
|
||||
Formbricks packs a lot of useful functionality out of the box. You can:
|
||||
|
||||
- Close the survey on a specidic date
|
||||
- After a number of response
|
||||
- Redirect users to a URL after they completed the survey
|
||||
- Protect survey with a Pin
|
||||
- ... and much more!
|
||||
|
||||
<Image
|
||||
src={SurveyResponseOptions}
|
||||
alt="Survey response configuration for link survey"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
## Style your survey
|
||||
|
||||
Style your survey to your need. You can keep it simplistic or use animated backgrounds. You can change the main color and soon you'll be able to fully control the appearance of the survey.
|
||||
|
||||
<Image
|
||||
src={SurveySettings}
|
||||
alt="UI & View configuration for link survey"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
## Publish your survey
|
||||
|
||||
Once you’re happy with the survey settings, hit **Publish** and you’ll be forwarded to the Summary Page. This is where you’ll find the responses to this survey.
|
||||
|
||||
<Image
|
||||
src={SurveyPublished}
|
||||
alt="Survey published successfully and received link to share with users"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
## Share your survey
|
||||
|
||||
Congratulations! Your survey is now published and ready to be shared with your users. You can share the survey link via email, SMS, or any other channel.
|
||||
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 32 KiB |
@@ -1,24 +0,0 @@
|
||||
export const metadata = {
|
||||
title: "Self-hosting License to run Formbricks on premises",
|
||||
description:
|
||||
"Request a self-hosting licenses to run Formbricks on your own servers.",
|
||||
};
|
||||
|
||||
#### Self-Hosting
|
||||
|
||||
# License Request (Beta)
|
||||
|
||||
We're handing out free self-hosting license keys during our Beta.
|
||||
|
||||
**Please note:** Sooner than later we will introduce a self-hosting license pricing. For a free beta key, fill out this form:
|
||||
|
||||
<div style={{ position: 'relative', height: '100vh', maxHeight: '100vh', overflow: 'auto', borderRadius:'12px' }}>
|
||||
<iframe
|
||||
src="https://app.formbricks.com/s/clrf4z8zg1u3912250j7shqb5"
|
||||
style={{ position: 'absolute', left: 0, top: 0, width: '100%', height: '100%', border: 0 }}
|
||||
>
|
||||
</iframe>
|
||||
</div>
|
||||
|
||||
|
||||
**Can’t figure it out?**: [Join our Discord!](https://formbricks.com/discord)
|
||||
@@ -29,7 +29,7 @@ Copy and paste the following command into your terminal:
|
||||
<CodeGroup title="Single Command to deploy Formbricks">
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/formbricks/formbricks/main/docker/formbricks.sh -o formbricks.sh && chmod +x formbricks.sh && ./formbricks.sh install
|
||||
curl -fsSL https://raw.githubusercontent.com/formbricks/formbricks/main/docker/production.sh -o formbricks.sh && chmod +x formbricks.sh && ./formbricks.sh install
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -219,16 +219,18 @@ The script will automatically stop all the Formbricks related containers, remove
|
||||
|
||||
## Debugging
|
||||
|
||||
If you encounter any issues, you can check the logs of the containers with:
|
||||
If you encounter any issues, you can check the logs of the container with:
|
||||
<Col>
|
||||
<CodeGroup title="Check logs of the container">
|
||||
|
||||
```bash
|
||||
./formbricks.sh logs
|
||||
cd formbricks && docker compose logs -f
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
You can close the logs again with `CTRL + C`.
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -242,4 +244,4 @@ If you encounter any issues, consider the following steps:
|
||||
|
||||
- **Check Formbricks Logs**: Run `cd formbricks && docker compose logs` to check the logs of the Formbricks stack.
|
||||
|
||||
**Can’t figure it out?**: [Join our Discord!](https://formbricks.com/discord)
|
||||
**Still can’t figure it out?**: [Join our Discord!](https://formbricks.com/discord)
|
||||
|
||||
@@ -189,20 +189,32 @@ export const navigation: Array<NavGroup> = [
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "In-App Surveys",
|
||||
title: "Getting Started",
|
||||
links: [
|
||||
{ title: "Quickstart", href: "/docs/getting-started/quickstart-in-app-survey" },
|
||||
{ title: "Quickstart: In app", href: "/docs/getting-started/quickstart-in-app-survey" },
|
||||
{ title: "Framework Guides", href: "/docs/getting-started/framework-guides" },
|
||||
{ title: "Troubleshooting", href: "/docs/getting-started/troubleshooting" },
|
||||
{ title: "Identify Users", href: "/docs/in-app-surveys/user-identification" },
|
||||
{ title: "Actions", href: "/docs/in-app-surveys/actions" },
|
||||
{ title: "Attributes", href: "/docs/in-app-surveys/attributes" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Attributes",
|
||||
links: [
|
||||
{ title: "Why Attributes?", href: "/docs/attributes/why" },
|
||||
{ title: "Custom Attributes", href: "/docs/attributes/custom-attributes" },
|
||||
{ title: "Identify users", href: "/docs/attributes/identify-users" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Actions",
|
||||
links: [
|
||||
{ title: "Why Actions?", href: "/docs/actions/why" },
|
||||
{ title: "No-Code Actions", href: "/docs/actions/no-code" },
|
||||
{ title: "Code Actions", href: "/docs/actions/code" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Link Surveys",
|
||||
links: [
|
||||
{ title: "Quickstart", href: "/docs/link-surveys/quickstart" },
|
||||
{ title: "Data Prefilling", href: "/docs/link-surveys/data-prefilling" },
|
||||
{ title: "Identify Users", href: "/docs/link-surveys/user-identification" },
|
||||
{ title: "Single Use Links", href: "/docs/link-surveys/single-use-links" },
|
||||
@@ -219,7 +231,6 @@ export const navigation: Array<NavGroup> = [
|
||||
{ title: "Feature Chaser", href: "/docs/best-practices/feature-chaser" },
|
||||
{ title: "Feedback Box", href: "/docs/best-practices/feedback-box" },
|
||||
{ title: "Docs Feedback", href: "/docs/best-practices/docs-feedback" },
|
||||
{ title: "Improve Email Content", href: "/docs/best-practices/improve-email-content" },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -242,7 +253,6 @@ export const navigation: Array<NavGroup> = [
|
||||
{ title: "Advanced Setup", href: "/docs/self-hosting/docker" },
|
||||
{ title: "Configure", href: "/docs/self-hosting/external-auth-providers" },
|
||||
{ title: "Migration Guide", href: "/docs/self-hosting/migration-guide" },
|
||||
{ title: "Self-hosting License", href: "/docs/self-hosting/license" },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -275,7 +285,6 @@ export const navigation: Array<NavGroup> = [
|
||||
{ title: "Attribute Classes", href: "/docs/api/management/attribute-classes" },
|
||||
{ title: "Me", href: "/docs/api/management/me" },
|
||||
{ title: "People", href: "/docs/api/management/people" },
|
||||
{ title: "Responses", href: "/docs/api/management/responses" },
|
||||
{ title: "Surveys", href: "/docs/api/management/surveys" },
|
||||
{ title: "Webhooks", href: "/docs/api/management/webhooks" },
|
||||
],
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import * as DOMPurify from "dompurify";
|
||||
/* import { cleanHtml } from "../../lib/cleanHtml"; */
|
||||
import { cleanHtml } from "@formbricks/lib/cleanHtml";
|
||||
|
||||
export default function HtmlBody({ htmlString, questionId }: { htmlString: string; questionId: string }) {
|
||||
return (
|
||||
<label
|
||||
htmlFor={questionId}
|
||||
className="fb-block fb-font-normal fb-leading-6 text-sm text-slate-500 dark:text-slate-300"
|
||||
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(htmlString) }}></label>
|
||||
dangerouslySetInnerHTML={{ __html: cleanHtml(htmlString) }}></label>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ export default function NPSQuestion({ question, onSubmit, lastQuestion, brandCol
|
||||
selectedChoice === number
|
||||
? "z-10 bg-slate-50 dark:bg-slate-500"
|
||||
: "dark:bg-slate-700 dark:hover:bg-slate-500",
|
||||
"relative h-10 flex-1 cursor-pointer border bg-white text-center text-sm leading-10 text-slate-900 first:rounded-l-md last:rounded-r-md hover:bg-slate-100 focus:outline-none dark:border-slate-600 dark:text-white "
|
||||
"relative h-10 flex-1 cursor-pointer border bg-white text-center text-sm leading-10 text-slate-900 first:rounded-l-md last:rounded-r-md hover:bg-gray-100 focus:outline-none dark:border-slate-600 dark:text-white "
|
||||
)}>
|
||||
<input
|
||||
type="radio"
|
||||
|
||||
@@ -56,7 +56,7 @@ export default function RatingQuestion({
|
||||
className={cn(
|
||||
selectedChoice === number
|
||||
? "z-10 border-slate-400 bg-slate-50"
|
||||
: "bg-white hover:bg-slate-100 dark:bg-slate-700 dark:hover:bg-slate-500",
|
||||
: "bg-white hover:bg-gray-100 dark:bg-slate-700 dark:hover:bg-slate-500",
|
||||
"relative h-10 flex-1 cursor-pointer border border-slate-100 text-center text-sm leading-10 text-slate-800 first:rounded-l-md last:rounded-r-md focus:outline-none dark:border-slate-500 dark:text-slate-200 "
|
||||
)}>
|
||||
<input
|
||||
|
||||
@@ -53,11 +53,12 @@ export default function TemplateList({ onTemplateClick, activeTemplate }: Templa
|
||||
{templates
|
||||
.filter((template) => selectedFilter === ALL_CATEGORY_NAME || template.category === selectedFilter)
|
||||
.map((template: TTemplate) => (
|
||||
<div
|
||||
key={template.name}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
onTemplateClick(template); // Pass the 'template' object instead of 'activeTemplate'
|
||||
}}
|
||||
key={template.name}
|
||||
className={cn(
|
||||
activeTemplate?.name === template.name && "ring-brand ring-2",
|
||||
"duration-120 group relative rounded-lg bg-white p-6 shadow transition-all duration-150 hover:scale-105 dark:bg-slate-700"
|
||||
@@ -70,7 +71,7 @@ export default function TemplateList({ onTemplateClick, activeTemplate }: Templa
|
||||
{template.name}
|
||||
</h3>
|
||||
<p className="text-left text-xs text-slate-600 dark:text-slate-400">{template.description}</p>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
DashboardIcon,
|
||||
DogChaserIcon,
|
||||
DoorIcon,
|
||||
EmailIcon,
|
||||
FeedbackIcon,
|
||||
GaugeSpeedFastIcon,
|
||||
HeartCommentIcon,
|
||||
@@ -1225,61 +1224,6 @@ export const templates: TTemplate[] = [
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Improve Newsletter Content",
|
||||
icon: EmailIcon,
|
||||
category: "Growth",
|
||||
description: "Find out how your subscribers like your newsletter content.",
|
||||
objectives: ["increase_conversion", "sharpen_marketing_messaging"],
|
||||
preset: {
|
||||
name: "Improve Newsletter Content",
|
||||
questions: [
|
||||
{
|
||||
id: createId(),
|
||||
type: TSurveyQuestionType.Rating,
|
||||
logic: [
|
||||
{ value: "5", condition: "equals", destination: "l2q1chqssong8n0xwaagyl8g" },
|
||||
{ value: "5", condition: "lessThan", destination: "k3s6gm5ivkc5crpycdbpzkpa" },
|
||||
],
|
||||
range: 5,
|
||||
scale: "smiley",
|
||||
headline: "How would you rate this weeks newsletter?",
|
||||
required: true,
|
||||
subheader: "",
|
||||
lowerLabel: "Meh",
|
||||
upperLabel: "Great",
|
||||
},
|
||||
{
|
||||
id: "k3s6gm5ivkc5crpycdbpzkpa",
|
||||
type: TSurveyQuestionType.OpenText,
|
||||
logic: [
|
||||
{ condition: "submitted", destination: "end" },
|
||||
{ condition: "skipped", destination: "end" },
|
||||
],
|
||||
headline: "What would have made this weeks newsletter more helpful?",
|
||||
required: false,
|
||||
placeholder: "Type your answer here...",
|
||||
inputType: "text",
|
||||
},
|
||||
{
|
||||
id: "l2q1chqssong8n0xwaagyl8g",
|
||||
html: '<p class="fb-editor-paragraph" dir="ltr"><span>Who thinks like you? You\'d do us a huge favor if you\'d share this weeks episode with your brain friend!</span></p>',
|
||||
type: TSurveyQuestionType.CTA,
|
||||
headline: "Thanks! ❤️ Spread the love with ONE friend.",
|
||||
required: false,
|
||||
buttonUrl: "https://formbricks.com",
|
||||
buttonLabel: "Happy to help!",
|
||||
buttonExternal: true,
|
||||
dismissButtonLabel: "Find your own friends",
|
||||
},
|
||||
],
|
||||
welcomeCard: welcomeCardDefault,
|
||||
thankYouCard: thankYouCardDefault,
|
||||
hiddenFields: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const findTemplateByName = (name: string): TTemplate | undefined => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import AuthorJohannes from "@/images/blog/johannes-co-founder-formbricks-small.jpg";
|
||||
import AuthorOla from "@/images/blog/ola-content-writer.jpg";
|
||||
import AuthorShubham from "@/images/blog/shubham-engineer.png";
|
||||
import Image from "next/image";
|
||||
|
||||
interface AuthorBoxProps {
|
||||
@@ -11,18 +10,12 @@ interface AuthorBoxProps {
|
||||
author: string;
|
||||
}
|
||||
|
||||
const authorImages = {
|
||||
Johannes: AuthorJohannes,
|
||||
Ola: AuthorOla,
|
||||
Shubham: AuthorShubham,
|
||||
};
|
||||
|
||||
export default function AuthorBox({ name, title, date, duration, author }: AuthorBoxProps) {
|
||||
return (
|
||||
<div className="mb-8 flex items-center space-x-4 rounded-lg border border-slate-200 bg-slate-100 px-6 py-3 dark:border-slate-700 dark:bg-slate-800">
|
||||
<Image
|
||||
className="m-0 rounded-full"
|
||||
src={authorImages[author]}
|
||||
src={author === "Johannes" ? AuthorJohannes : AuthorOla}
|
||||
alt={name}
|
||||
width={45}
|
||||
height={45}
|
||||
|
||||
@@ -79,15 +79,6 @@ export default function BestPracticeNavigation() {
|
||||
description: "Give users the chance to share feedback in a single click.",
|
||||
category: "Boost Retention",
|
||||
},
|
||||
|
||||
{
|
||||
name: "Improve Newsletter Content",
|
||||
href: "/improve-newsletter-content",
|
||||
status: true,
|
||||
icon: FeedbackIcon,
|
||||
description: "Improve your newsletter content by showing this survey to your readers.",
|
||||
category: "Boost Retention",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
|
||||
@@ -60,7 +60,7 @@ export default function LayoutMdx({ meta, children }: Props) {
|
||||
)}
|
||||
</header>
|
||||
)}
|
||||
<Prose className="prose-h2:text-2xl prose-li:text-base prose-h2:mt-6 prose-p:text-base prose-p:mb-4 prose-h3:text-xl prose-a:text-slate-900 prose-a:hover:text-slate-900 prose-a:text-decoration-brand prose-a:not-italic prose-ul:pl-12">
|
||||
<Prose className="prose-h2:text-2xl prose-li:text-base prose-h2:mt-4 prose-p:text-base prose-p:mb-4 prose-h3:text-xl prose-a:text-slate-900 prose-a:hover:text-slate-900 prose-a:text-decoration-brand prose-a:not-italic prose-ul:pl-12">
|
||||
{children}
|
||||
</Prose>
|
||||
</article>
|
||||
|
||||
|
Before Width: | Height: | Size: 219 KiB |
97
apps/formbricks-com/lib/cleanHtml.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/*!
|
||||
* Sanitize an HTML string
|
||||
* (c) 2021 Chris Ferdinandi, MIT License, https://gomakethings.com
|
||||
* @param {String} str The HTML string to sanitize
|
||||
* @return {String} The sanitized string
|
||||
*/
|
||||
export function cleanHtml(str: string): string {
|
||||
/**
|
||||
* Convert the string to an HTML document
|
||||
* @return {Node} An HTML document
|
||||
*/
|
||||
function stringToHTML() {
|
||||
let parser = new DOMParser();
|
||||
let doc = parser.parseFromString(str, "text/html");
|
||||
return doc.body || document.createElement("body");
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove <script> elements
|
||||
* @param {Node} html The HTML
|
||||
*/
|
||||
function removeScripts(html: Element) {
|
||||
let scripts = html.querySelectorAll("script");
|
||||
scripts.forEach((script) => {
|
||||
script.remove();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the attribute is potentially dangerous
|
||||
* @param {String} name The attribute name
|
||||
* @param {String} value The attribute value
|
||||
* @return {Boolean} If true, the attribute is potentially dangerous
|
||||
*/
|
||||
/**
|
||||
* Check if the attribute is potentially dangerous
|
||||
*/
|
||||
function isPossiblyDangerous(name: string, value: string): boolean {
|
||||
let val = value.replace(/\s+/g, "").toLowerCase();
|
||||
if (
|
||||
["src", "href", "xlink:href", "srcdoc"].includes(name) &&
|
||||
(val.includes("javascript:") || val.includes("data:") || val.includes("<script>"))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (name.startsWith("on")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove potentially dangerous attributes from an element
|
||||
* @param {Node} elem The element
|
||||
*/
|
||||
function removeAttributes(elem: Element) {
|
||||
// Loop through each attribute
|
||||
// If it's dangerous, remove it
|
||||
let atts = elem.attributes;
|
||||
for (let i = atts.length - 1; i >= 0; i--) {
|
||||
let { name, value } = atts[i];
|
||||
if (isPossiblyDangerous(name, value)) {
|
||||
elem.removeAttribute(name);
|
||||
} else if (name === "srcdoc") {
|
||||
// Recursively sanitize srcdoc content
|
||||
elem.setAttribute(name, cleanHtml(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove dangerous stuff from the HTML document's nodes
|
||||
* @param {Node} html The HTML document
|
||||
*/
|
||||
/**
|
||||
* Clean the HTML nodes recursively
|
||||
* @param {Element} html The HTML element
|
||||
*/
|
||||
function clean(html: Element) {
|
||||
let nodes = Array.from(html.children);
|
||||
for (let node of nodes) {
|
||||
removeAttributes(node);
|
||||
clean(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the string to HTML
|
||||
let html = stringToHTML();
|
||||
|
||||
// Sanitize it
|
||||
removeScripts(html);
|
||||
clean(html);
|
||||
|
||||
// If the user wants HTML nodes back, return them
|
||||
// Otherwise, pass a sanitized string back
|
||||
return html.innerHTML;
|
||||
}
|
||||
@@ -1,18 +1,19 @@
|
||||
export const handleFeedbackSubmit = async (YesNo: string, pageUrl: string | null) => {
|
||||
const response_data = {
|
||||
isHelpful: YesNo,
|
||||
pageUrl: pageUrl,
|
||||
data: {
|
||||
isHelpful: YesNo,
|
||||
pageUrl: pageUrl,
|
||||
},
|
||||
};
|
||||
|
||||
const payload = {
|
||||
response: response_data,
|
||||
surveyId: process.env.NEXT_PUBLIC_FORMBRICKS_COM_DOCS_FEEDBACK_SURVEY_ID,
|
||||
finished: true,
|
||||
data: response_data,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_FORMBRICKS_COM_API_HOST}/api/v1/client/${process.env.NEXT_PUBLIC_FORMBRICKS_COM_ENVIRONMENT_ID}/responses`,
|
||||
`${process.env.NEXT_PUBLIC_FORMBRICKS_COM_API_HOST}/api/v1/client/environments/${process.env.NEXT_PUBLIC_FORMBRICKS_COM_ENVIRONMENT_ID}/responses`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import nextMDX from "@next/mdx";
|
||||
import { withPlausibleProxy } from "next-plausible";
|
||||
|
||||
import { withPlausibleProxy } from "next-plausible";
|
||||
import { recmaPlugins } from "./mdx/recma.mjs";
|
||||
import { rehypePlugins } from "./mdx/rehype.mjs";
|
||||
import { remarkPlugins } from "./mdx/remark.mjs";
|
||||
@@ -155,6 +155,11 @@ const nextConfig = {
|
||||
destination: "/docs/self-hosting/migration-guide",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/cla",
|
||||
destination: "https://formbricks.com/clmyhzfrymr4ko00hycsg1tvx",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/contributing/gitpod",
|
||||
destination: "/docs/contributing/setup#gitpod",
|
||||
@@ -165,41 +170,6 @@ const nextConfig = {
|
||||
destination: "/community",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/actions/why",
|
||||
destination: "/docs/in-app-surveys/actions",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/actions/no-code",
|
||||
destination: "/docs/in-app-surveys/actions#no-code-actions",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/actions/code",
|
||||
destination: "/docs/in-app-surveys/actions#code-actions",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/attributes/why",
|
||||
destination: "/docs/in-app-surveys/attributes",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/attributes/custom-attributes",
|
||||
destination: "/docs/in-app-surveys/attributes#setting-custom-user-attributes",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/docs/attributes/identify-users",
|
||||
destination: "/docs/in-app-surveys/attributes#identifying-users",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/signup",
|
||||
destination: "https://app.formbricks.com/auth/signup",
|
||||
permanent: true,
|
||||
},
|
||||
];
|
||||
},
|
||||
async rewrites() {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"browserslist": "defaults, not ie <= 11",
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-core": "^1.13.0",
|
||||
"@algolia/autocomplete-core": "^1.11.0",
|
||||
"@calcom/embed-react": "^1.3.0",
|
||||
"@docsearch/react": "^3.5.2",
|
||||
"@formbricks/lib": "workspace:*",
|
||||
@@ -20,51 +20,50 @@
|
||||
"@formbricks/ui": "workspace:*",
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@heroicons/react": "^2.1.1",
|
||||
"@mapbox/rehype-prism": "^0.9.0",
|
||||
"@mdx-js/loader": "^3.0.0",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"@next/mdx": "14.0.4",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@mapbox/rehype-prism": "^0.8.0",
|
||||
"@mdx-js/loader": "^2.3.0",
|
||||
"@mdx-js/react": "^2.3.0",
|
||||
"@next/mdx": "13.4.19",
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"@radix-ui/react-slider": "^1.1.2",
|
||||
"@radix-ui/react-tooltip": "^1.0.6",
|
||||
"@sindresorhus/slugify": "^2.2.1",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/react-highlight-words": "^0.16.5",
|
||||
"acorn": "^8.10.0",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"clsx": "^2.0.0",
|
||||
"fast-glob": "^3.3.1",
|
||||
"flexsearch": "^0.7.31",
|
||||
"framer-motion": "10.17.8",
|
||||
"framer-motion": "10.16.4",
|
||||
"lottie-web": "^5.12.2",
|
||||
"mdast-util-to-string": "^4.0.0",
|
||||
"mdx-annotations": "^0.1.4",
|
||||
"mdx-annotations": "^0.1.3",
|
||||
"next": "13.4.19",
|
||||
"next-plausible": "^3.12.0",
|
||||
"next-seo": "^6.4.0",
|
||||
"next-plausible": "^3.11.1",
|
||||
"next-seo": "^6.1.0",
|
||||
"next-sitemap": "^4.2.3",
|
||||
"next-themes": "^0.2.1",
|
||||
"node-fetch": "^3.3.2",
|
||||
"prism-react-renderer": "^2.3.1",
|
||||
"prism-react-renderer": "^2.0.6",
|
||||
"prismjs": "^1.29.0",
|
||||
"@radix-ui/react-slider": "^1.1.2",
|
||||
"@radix-ui/react-tooltip": "^1.0.6",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-highlight-words": "^0.20.0",
|
||||
"react-icons": "^4.12.0",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-icons": "^4.11.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-responsive-embed": "^2.1.0",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-mdx": "^3.0.0",
|
||||
"sharp": "^0.33.1",
|
||||
"shiki": "^0.14.7",
|
||||
"remark": "^14.0.3",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-mdx": "^2.3.0",
|
||||
"sharp": "^0.32.5",
|
||||
"shiki": "^0.14.4",
|
||||
"simple-functional-loader": "^1.2.1",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"unist-util-filter": "^5.0.1",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"zustand": "^4.4.7"
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
|
||||
@@ -10,12 +10,6 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
description: "Build build custom software on top of your data.",
|
||||
href: "https://www.appsmith.com",
|
||||
},
|
||||
{
|
||||
name: "Aptabase",
|
||||
description:
|
||||
"Analytics for Apps, open source, simple and privacy-friendly. SDKs for Swift, React Native, Electron, Flutter and many others.",
|
||||
href: "https://aptabase.com",
|
||||
},
|
||||
{
|
||||
name: "Argos",
|
||||
description: "Argos provides the developer tools to debug tests and detect visual regressions..",
|
||||
@@ -122,7 +116,8 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
},
|
||||
{
|
||||
name: "Lost Pixel",
|
||||
description: "Open source visual regression testing alternative to Percy & Chromatic",
|
||||
description:
|
||||
"Open source visual regression testing alternative to Percy & Chromatic",
|
||||
href: "https://lost-pixel.com",
|
||||
},
|
||||
{
|
||||
|
||||
|
Before Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 85 KiB |
@@ -1,309 +0,0 @@
|
||||
import AuthorBox from "@/components/shared/AuthorBox";
|
||||
import LayoutMdx from "@/components/shared/LayoutMdx";
|
||||
import Image from "next/image";
|
||||
|
||||
import CrazyEgg from './crazy-egg-website-optimization-heatmaps-recordings-surveys.png'
|
||||
import CoverImage from './cover-best-feedbackt-tools-2024-open-source-website-surveys-targeted.webp'
|
||||
import Formbricks from './formbricks-privacy-first-experience-management.png'
|
||||
import Hotjar from './hotjar-website-heatmaps-behavior-analytics-tools.png'
|
||||
import IdeaScale from './idea-and-innovation-management-software-ideaScale.png'
|
||||
import Mopinion from './mopinion-feedback-for-websites-apps-and-email.png'
|
||||
import Sprinklr from './sprinklr-unified-customer-experience-management-platform-sprinklr.png'
|
||||
import SurveyMonkey from './surveyMonkey-the-world-most-popular-free-online-survey-tool.png'
|
||||
import Qualaroo from './user-research-customer-feedback-software-qualaroo.png'
|
||||
import UserReport from './userReport-simple-user-engagement-tools-that-help-you-improve.png'
|
||||
import UserSnap from './usersnap-your-number-one-user-feedback-platform.png'
|
||||
|
||||
export const meta = {
|
||||
title: "Best Website Feedback Tools in 2024",
|
||||
description:
|
||||
"The best website feedback tools play a crucial role in both enhancing the user experience and optimizing conversion rates. In this article, we cover 10 of the recommended ones.",
|
||||
date: "2024-01-16",
|
||||
publishedTime: "2024-01-16T12:00:00",
|
||||
authors: ["Olasunkanmi Balogun"],
|
||||
section: "Website Feedback Tools",
|
||||
tags: ["Feedback Tools", "Formbricks", "SurveyMonkey", "Mopinion", "Qualaroo", "IdeaScale", "UserReport", "Sprinklr", "Hotjar", "UserSnap", "CrazyEgg"],
|
||||
};
|
||||
|
||||
<Image src={CoverImage} alt="Best Open Source Website Feedback Tools 2024" className="w-full rounded-lg" />
|
||||
|
||||
<AuthorBox
|
||||
name="Olasunkanmi Balogun"
|
||||
title="Content Writer"
|
||||
date="January 16th, 2024"
|
||||
duration="10"
|
||||
author={"Ola"}
|
||||
/>
|
||||
|
||||
As users navigate your website, their feedback serves as a compass, guiding you toward improvements that resonate with them. Feedback from users provides valuable insights into areas where they might be facing challenges or experiencing dissatisfaction. This information is instrumental in identifying and rectifying pain points within your website's design, navigation, or functionality.
|
||||
|
||||
But how do you get this feedback?
|
||||
|
||||
That’s where **website feedback tools** come into play. The best website feedback tools play a crucial role in both enhancing the user experience and optimizing conversion rates. These tools are usually equipped with features like:
|
||||
|
||||
- Targeted surveys and
|
||||
- Visual feedback options like screen recordings and
|
||||
|
||||
However, choosing the right tool is where the problem may lie. Among the myriad of website feedback tools, this article will uncover 10 of the top recommended tools, each distinguished by a unique feature that sets it apart from the rest.
|
||||
|
||||
The water's fine; let's dive in headfirst. 🤸
|
||||
|
||||
## The 10 Best Website Feedback Tools 2024
|
||||
|
||||
We’ll categorize the website tools highlighted in this section into three categories.
|
||||
|
||||
1. Website Voice Of The Customer (VoC) Feedback Tools
|
||||
2. Community Feedback Tools
|
||||
3. Behavioral Analysis Tools
|
||||
|
||||
Website VoC Feedback Tools focus on understanding and enhancing user experiences; Community Feedback Tools emphasize collaboration and innovation; and Behavioral Analysis Tools provide deep insights for informed decision-making. These tools ensure businesses not only meet user expectations but also stay agile and competitive in the evolving online environment.
|
||||
|
||||
As we explore each tool within these categories, we'll explore their vital roles in the digital landscape, their unique approach to how they offer their services and their shortcomings.
|
||||
|
||||
## Website voice of the customer (VoC) feedback tools
|
||||
|
||||
These tools, as we’ll see, are tools that help you collect and analyze the opinions and experiences of your website visitors. They gather user data through surveys, providing valuable insights into how users interact with your website and what they think about it. The surveys may take various forms, including CSAT, CES, and NPS, as well as polls and quizzes.
|
||||
|
||||
### Formbricks
|
||||
|
||||
<Image
|
||||
src={Formbricks}
|
||||
alt="Formbricks is a free and open source survey software for in app micro surveys. Ask any user segment at any point in the user journey."
|
||||
className="rounded-lg w-full"
|
||||
/>
|
||||
|
||||
Formbricks is a unique open-source experience management platform designed to facilitate the collection of qualitative user feedback through highly customizable surveys at various touchpoints in your users' journey. It allows you to create multiple survey types, including CSAT, NPS, and CES, to gather insights effectively.
|
||||
|
||||
These surveys can be seamlessly embedded into your design, ensuring feedback is collected at key moments without disrupting the user flow. Alternatively, you can share survey links to gather feedback from diverse online sources, such as social media or blog posts.
|
||||
|
||||
#### Unique Approach
|
||||
|
||||
Formbricks stands out in its trigger versatility by offering an array of generic triggers to initiate surveys. These triggers include New Session, Exit Intent, 50% scroll, created new event, updated
|
||||
accessibility, received booking requests, invited team member, created new workflow, and viewed insights. This flexibility ensures your surveys appear at precise moments of relevance to maximize their impact.
|
||||
|
||||
Moreover, Formbricks takes customization a step further by allowing users to create bespoke triggers tailored to specific needs. Whether it's based on the URL, innerText, or CSS selectors, you can set up triggers that align precisely with your unique goals. This allows you to trigger surveys based on the exact content the user sees or even interactions with specific buttons or sections.
|
||||
|
||||
Additionally, the platform provides actionable insights through intuitive dashboards, making it easy to derive meaningful conclusions from the collected feedback.
|
||||
|
||||
#### Shortcomings
|
||||
|
||||
Formbricks is still very young and doesn’t integrate with as many
|
||||
third-party tools, although it integrates with popular third-party tools
|
||||
like Slack, Make.com, and Notion. However, the Formbricks team and open
|
||||
source community is working on adding more all the time.
|
||||
|
||||
**Overall**, Formbricks is built on the largest open source survey infrastructure on GitHub. This ensures a privacy-first approach to gathering and analyzing user feedback. You can [get started](http://formbricks.com/) with a free plan and experience that modern open source software can be even more user-friendly than proprietary solutions.
|
||||
|
||||
### SurveyMonkey
|
||||
|
||||
<Image
|
||||
src={SurveyMonkey}
|
||||
alt="SurveyMonkey is a widely used survey platform offering a robust suite of survey features to cater to diverse needs and research goals."
|
||||
className="rounded-lg w-full"
|
||||
/>
|
||||
|
||||
SurveyMonkey is a widely used survey platform offering a robust suite of survey features to cater to diverse needs and research goals.
|
||||
|
||||
#### Unique Approach
|
||||
|
||||
With SurveyMonkey, you have the flexibility to start building your surveys from scratch or utilize the AI (which is backed by openAI) to build templates for you. All you need to do is write a prompt that
|
||||
includes your survey goals, and AI will do the rest.
|
||||
|
||||
It also enables you to integrate your survey data with over 175 third-party tools in your stack, including CMS and marketing automation tools, among others.
|
||||
|
||||
#### Shortcoming
|
||||
|
||||
SurveyMonkey has been around for over 20 years and looks and feels like that. While it is a proven solution for enterprise buyers, most smaller companies might find it bulky.
|
||||
|
||||
**Overall**, whether you are an individual, a business, or even an educator who needs to design polls and quizzes for classroom engagement and gather student feedback, SurveyMonkey can do it.
|
||||
|
||||
### Mopinion
|
||||
|
||||
<Image
|
||||
src={Mopinion}
|
||||
alt="Mopinion is a user feedback software with features to create online surveys."
|
||||
className="rounded-lg w-full"
|
||||
/>
|
||||
|
||||
Mopinion is a user feedback software with features to create online surveys. With Mopinion, you don’t just get feedback; it also offers in-depth analysis opportunities on the feedback for you through data visualization on a customizable dashboard.
|
||||
|
||||
#### Unique Approach
|
||||
|
||||
What's special about Mopinion is its user-friendly features, like drag-and-drop when you're building your forms. You can make feedback forms for all your digital channels easily. One unique thing is that it offers a fully native SDK for Android and iOS, meaning you can put it right into your app. And if you're into self-hosting to keep more control over your data, Mopinion lets you create self-hosted surveys too.
|
||||
|
||||
#### Shortcoming
|
||||
|
||||
While Mopinion offers robust features, businesses may find the learning curve for the advanced functionalities quite steep. Users may need some time to familiarize themselves with the platform's
|
||||
capabilities, especially when creating customized surveys or integrating with various third-party tools
|
||||
|
||||
**Overall**, Mopinion plays well with other tools too. It integrates smoothly with third-party tools such as Slack, Google Analytics, Jira, Hubspot, Salesforce, and many more.
|
||||
|
||||
### Qualaroo
|
||||
|
||||
<Image
|
||||
src={Qualaroo}
|
||||
alt="Qualaroo is a user feedback and survey platform designed to help businesses understand the needs and preferences of their website visitors. Qualaroo is geared towards capturing real-time insights from website users."
|
||||
className="rounded-lg w-full"
|
||||
/>
|
||||
Qualaroo is a user feedback and survey platform designed to help businesses understand the needs and preferences of their website visitors. Qualaroo is geared towards capturing real-time insights from website users to enhance the user experience and inform business decisions.
|
||||
|
||||
It does this through something called "Nudges" – these are subtle little surveys that pop up at just the right times on your website. They don't interrupt what visitors are doing; instead, they smoothly blend into the user's journey.
|
||||
|
||||
With Qualaroo, you have different survey types to choose from, like custom surveys, NPS (Net Promoter Score), PMF (Product-Market Fit), and on-site surveys. Plus, there's a special dashboard just for NPS surveys that lets you keep an eye on scores in real time.
|
||||
|
||||
What's cool is that you can customize these surveys to target specific user behaviors. You can ask for feedback based on where users are, who they are, when they visit, and more; it's like tailoring questions to fit the person.
|
||||
|
||||
#### Unique Approach
|
||||
|
||||
Qualaroo stands out with its innovative approach to gathering user insights. It leverages unobtrusive survey "Nudges" that appear at strategic moments on a website.
|
||||
|
||||
#### Shortcoming
|
||||
|
||||
There may be a risk of feedback bias since users can choose whether or not to engage with the Nudges.
|
||||
|
||||
**Overall**, Qualaroo plays well with other popular tools that businesses use. So, whether you're a small startup or a big corporation, Qualaroo is here to make your website the best it can be.
|
||||
|
||||
### 2. Community Feedback Tools
|
||||
|
||||
These are platforms designed to facilitate and manage feedback from a community of users or customers of a business. With these tools, you can gather opinions, suggestions, and insights from a group of people who share a common interest or connection, such as users of a particular
|
||||
product, members of an online community, or participants in a specific project.
|
||||
|
||||
### IdeaScale
|
||||
|
||||
<Image
|
||||
src={IdeaScale}
|
||||
alt="IdeaScale is an innovation management solution that links organizations to people with ideas."
|
||||
className="rounded-lg w-full"
|
||||
/>
|
||||
|
||||
IdeaScale describes itself as an innovation management solution that links organizations to people with ideas.
|
||||
|
||||
### Unique Approach
|
||||
|
||||
IdeaScale stands out in its approach to connecting organizations with people who contribute ideas by providing an idea hub for users to submit and discuss ideas openly. Some of its other features on this platform include an idea submission feature, which enables you to easily share an idea and add all the relevant details so that anyone can view, validate, or add to that idea; a similarity search to reduce redundancy by proactively searching for similar ideas; and idea linking and annotations to identify trends and relationships between ideas by linking ideas and showcasing how they build on one another.
|
||||
|
||||
### Shortcoming
|
||||
|
||||
Nonetheless, while IdeaScale offers valuable features, it presents the challenge of managing large volumes of ideas. As the platform encourages open submission, there might be a need for effective mechanisms to sift through and prioritize ideas, ensuring that valuable contributions receive due attention.
|
||||
|
||||
**Overall,** IdeaScale facilitates the collaborative exchange of insights, making it a valuable tool for businesses seeking feedback and innovation from their community.
|
||||
|
||||
### UserReport
|
||||
|
||||
<Image
|
||||
src={UserReport}
|
||||
alt="UserReport helps you resonate with your users."
|
||||
className="rounded-lg w-full"
|
||||
/>
|
||||
|
||||
UserReport helps you resonate with your users. It tells you who your users are and what they want to achieve. It is based primarily on two features, the survey widget and the feedback widget.
|
||||
|
||||
#### Unique Approach
|
||||
|
||||
Through its survey widget, the tool gathers essential information about your website visitors. However, it's the feedback widget that transforms UserReport into a dynamic space for community engagement. Here, users actively participate by posting, voting on, and discussing ideas. This active involvement shifts the tool from a passive data collector to a platform where users contribute thoughts and opinions, essentially becoming co-creators in shaping the direction of your
|
||||
platform.
|
||||
|
||||
#### Shortcoming
|
||||
|
||||
However, as with any community-driven tool, managing and prioritizing a large volume of user-generated content may pose a challenge. Additionally, the effectiveness of UserReport relies heavily on users actively participating in surveys, providing feedback, and contributing ideas. Hence, if user engagement is low, the platform may not harness its full potential.
|
||||
|
||||
**Overall**, with UserReport, you're not just getting data; you're building a bridge to your users' thoughts and creating a vibrant community where everyone's voice matters.
|
||||
|
||||
### Sprinklr Social
|
||||
|
||||
<Image
|
||||
src={Sprinklr}
|
||||
alt="Sprinklr is a platform for unified customer experience management."
|
||||
className="rounded-lg w-full"
|
||||
/>
|
||||
|
||||
Sprinklr boasts of being the first platform purpose-built for unified customer experience management.
|
||||
|
||||
#### Unique Approach
|
||||
|
||||
It introduces the concept of social listening through its comprehensive product suite, notably Sprinklr Social, a standout among G2’s Top 50 Products for Marketers. Unlike traditional customer experience platforms, Sprinklr Social goes beyond managing online presence; it delves into social listening, actively engaging businesses with their audience.
|
||||
|
||||
Sprinklr Social is a comprehensive tool that helps businesses manage their online presence, engage with their audience, and gather valuable insights just from its centralized dashboard.
|
||||
|
||||
#### Shortcoming
|
||||
|
||||
However, despite Sprinklr’s strength, businesses might face a learning curve in fully harnessing the platform's capabilities. The need for training and support to navigate and utilize all aspects efficiently could be a consideration before using this tool.
|
||||
|
||||
In essence, Sprinklr serves as a community feedback tool by not only allowing businesses to listen to their audience but also by actively engaging with the community, gathering feedback, and fostering meaningful interactions across various social media platforms.
|
||||
|
||||
### 3. Behavioural Analysis Tools
|
||||
|
||||
These tools go beyond traditional analytics tools that track page views and clicks. They go deeper into how a user behaves on your website, user motivations, and pain points. A typical behavioral
|
||||
analysis tool will offer features like session recordings, heatmaps, and funnels.
|
||||
|
||||
Let’s have a look at three of the recommended ones.
|
||||
|
||||
### Hotjar
|
||||
|
||||
<Image
|
||||
src={Hotjar}
|
||||
alt="Hotjar is a powerful behavioral analysis tool that lets you see, understand, and optimize how people interact with your website."
|
||||
className="rounded-lg w-full"
|
||||
/>
|
||||
|
||||
Hotjar is a powerful behavioral analysis tool that lets you see, understand, and optimize how people interact with your website.
|
||||
|
||||
#### Unique Approach
|
||||
|
||||
With Hotjar, when you sign up, you get several behavior analytics tools all at once, all from the same dashboard. These tools, including heatmaps and recordings, help you see what’s happening with users on your website.
|
||||
|
||||
#### Shortcoming
|
||||
|
||||
A potential shortcoming of Hotjar lies in its data sampling approach, particularly in standard plans. Users are limited to reviewing only a fraction of the entire dataset collected daily. For instance, if you have 3000 recordings per month, they are restricted to analyzing just 100 daily sessions. This sampling limitation could pose challenges for businesses aiming to conduct a comprehensive analysis of user interactions, potentially impacting the depth and accuracy of insights derived from the collected data.
|
||||
|
||||
**Overall**, Hotjar integrates with a wide range of third-party tools you may already be using. This makes it particularly suitable for you if you are an enterprise with diverse third-party tools in your tech stack, as it complements and enhances your existing analytics ecosystem.
|
||||
|
||||
### Usersnap
|
||||
|
||||
<Image
|
||||
src={UserSnap}
|
||||
alt="Usersnap is a visual feedback and bug-tracking tool that helps you to capture, organize, respond to, and scale user feedback all in one place."
|
||||
className="rounded-lg w-full"
|
||||
/>
|
||||
|
||||
Usersnap is a visual feedback and bug-tracking tool that helps you to capture, organize, respond to, and scale user feedback all in one place.
|
||||
|
||||
#### Unique Approach
|
||||
|
||||
At its core, Usersnap revolutionizes user interaction insights by offering a robust platform for capturing screenshots and annotating them. This visual feedback becomes a crucial lens through which development and design teams can analyze how users engage with various elements on a website, gaining qualitative insights into preferences, pain points, and areas of interest.
|
||||
|
||||
#### Shortcoming
|
||||
|
||||
Some of its users complain of the absence of search functionality or filtering options within the list of submitted feedback. Users navigating through their feedback submissions cannot efficiently search for specific types of feedback or filter submissions based on criteria. This limitation can hinder users' ability to quickly locate and review particular feedback instances or determine whether they have previously submitted similar feedback.
|
||||
|
||||
**Overall,** Usersnap integrates with popular collaboration tools such as Jira, Slack, and Trello. This ensures that the behavioral insights gleaned from Usersnap are seamlessly woven into broader project workflows, fostering collaboration and informed decision-making.
|
||||
|
||||
### CrazyEgg
|
||||
|
||||
<Image
|
||||
src={CrazyEgg}
|
||||
alt="CrazyEgg gives you visual insights into how people interact with your website, helping you see patterns, identify pain points, and optimize for success."
|
||||
className="rounded-lg w-full"
|
||||
/>
|
||||
|
||||
CrazyEgg gives you visual insights into how people interact with your website, helping you see patterns, identify pain points, and optimize for success.
|
||||
|
||||
#### Unique Approach
|
||||
|
||||
With CrazyEgg session recordings, you can watch your users click, scroll, and navigate through your website just like you are virtually standing right beside them.
|
||||
|
||||
On the other hand, CrazyEgg boasts of being the originator of webpage heatmap technology. It takes a snapshot of your web pages and creates heatmaps showing where users focus the most. But that's not all – you also get four other reports: the Scrollmap report, Confetti report, Overlay report, and List report. Each report gives you more insights into how users engage with your site. If you're curious about these reports, you can check out their website for detailed information.
|
||||
|
||||
#### Shortcoming
|
||||
|
||||
A notable challenge reported by users of CrazyEgg is the perceived lack of detailed reporting within the platform. Feedback suggests that the existing reporting features may not provide users with sufficient depth of information, leaving them wanting more comprehensive insights. The perceived limitation in detailed reporting could impact the platform's effectiveness for users who seek in-depth analytics to drive strategic improvements on their websites.
|
||||
|
||||
**Overall**, CrazyEgg stands out for its innovative approach to behavioral analysis and its role in enhancing website optimization strategies.
|
||||
|
||||
## In Conclusion
|
||||
|
||||
Website feedback tools offer businesses many possibilities to make websites better for users and increase how many people take desired actions. One tool, CrazyEgg, gives visual insights and real-time feedback, helping businesses understand how users interact with their websites. It lets you watch users move through the website and see patterns with session recordings and heatmaps.
|
||||
|
||||
Each tool, like Formbricks, plays a crucial role in improving digital experiences. Formbricks, especially, is unique as it is built on an open-source platform. It allows businesses to create flexible and customizable surveys for users. Being open source means it encourages collaboration, letting businesses adapt and contribute to its development. This approach emphasizes transparency and community-driven innovation, ensuring a user-friendly digital space.
|
||||
|
||||
export default ({ children }) => <LayoutMdx meta={meta}>{children}</LayoutMdx>;
|
||||
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 92 KiB |
@@ -1,165 +0,0 @@
|
||||
import AuthorBox from "@/components/shared/AuthorBox";
|
||||
import { Callout } from "@/components/shared/Callout";
|
||||
import LayoutMdx from "@/components/shared/LayoutMdx";
|
||||
import Image from "next/image";
|
||||
import NewsletterSurveyCode from './code-for-embedding-newsletter-survey-formbricks.png';
|
||||
import Header from './header-newsletter-survey-content-open-source-kpi.webp';
|
||||
import NewsletterSurvey from './improve-newsletter-content-editor-formbricks.png';
|
||||
import NewsletterSurveyModal from './improve-newsletter-content-modal-formbricks.png';
|
||||
import SurveyResponses from './survey-responses.png';
|
||||
import YourSurveys from './your-surveys-formbricks.png';
|
||||
export const meta = {
|
||||
title: "How to measure the quality of your newsletter content with Formbricks",
|
||||
description:
|
||||
"Newsletters boast the highest ROI in email marketing but grasping your audience's resonance is key. Formbricks steps in to empower you with tools to measure your content quality and explore reader engagement in newsletters.",
|
||||
date: "2024-01-24",
|
||||
publishedTime: "2024-01-24T12:00:00",
|
||||
authors: ["Olasunkanmi Balogun"],
|
||||
section: "Newsletter Survey",
|
||||
tags: ["Improve Newsletter Content"],
|
||||
};
|
||||
|
||||
<Image src={Header} alt="Gather in app feedback for free with these 6 tools." className="w-full rounded-lg" />
|
||||
|
||||
<AuthorBox
|
||||
name="Olasunkanmi Balogun"
|
||||
title="Content Writer"
|
||||
date="January 24th, 2024"
|
||||
duration="5"
|
||||
author={"Ola"}
|
||||
/>
|
||||
|
||||
_Newsletters are a form of email marketing which has the highest ROI of all marketing channels. According to the research (source below), for every dollar spent you can get a return of $36 to $40._
|
||||
|
||||
You reap this sweet, juciy ROI if your content resonates with your audience. You can only manage what you measure, so here is a quick, free and easy way to measure the quality of your newsletter content.
|
||||
|
||||
After this article, you have everything you need to easily measure the quality of your emails. We walk through you the setup, how to embed it in your email and what to do with the insights.
|
||||
|
||||
Here’s what we’ll cover:
|
||||
|
||||
1. **How link prefilling and user identification maximize your insights**
|
||||
2. **How to create the right survey with Formbricks**
|
||||
3. **Embeddung the survey in your email**
|
||||
4. **How to improve the analysis by identifying users**
|
||||
5. **How to enrich the response with hidden fields**
|
||||
6. **Auto-create surveys via API**
|
||||
|
||||
Toes are in the water, let’s dive deep 🏊
|
||||
|
||||
<Callout title="What Is Formbricks" type="note">
|
||||
Formbricks is an experience management suite built on the **largest open source survey stack** worldwide. It’s easy to use and packs all integrations you'd need.
|
||||
When you communicate via email, Formbricks bridges the gap between you and your readers. It packs everything you need to measure how happy users are with your newsletter content.
|
||||
</Callout>
|
||||
|
||||
## How link prefilling and user identification maximize your insights
|
||||
|
||||
To effectively measure the quality of your newsletter, you need:
|
||||
|
||||
- **Survey Embed Code:** A code snippet to embed the survey in the email (provided by Formbricks)
|
||||
- **Link Prefilling**: So that if a reader clicks on one of the stars or smileys in your rating survey, the data is stored and the first question is skipped (more on this later).
|
||||
- **Dynamic User Identification:** You can append the email of the reader dynamically to the survey to automatically create a personal profile for the reader. Hence, if this person provides any other feedback in their customer lifetime, it is gathered in the person's view.
|
||||
|
||||
Let's see how to stack these features to get what you're looking for 🤓
|
||||
|
||||
## How to create the right survey with Formbricks
|
||||
|
||||
1. After signing up on the Formbricks platform, you’ll be navigated to your dashboard. To create a survey for your newsletter, select the **Growth** filter and choose the “**Improve Newsletter Content**” survey template.
|
||||
|
||||
<Image
|
||||
src={YourSurveys}
|
||||
alt="Formbricks growth survey templates"
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
2. You’ll be directed to a page that will be your canvas for customizing your survey questions. For your survey’s first question, you can display smileys, stars, or even numbers for your survey rating; in the image below, we used a smiley.
|
||||
|
||||
<Callout title="Dynamic Follow-up Questions with Logic Jumps" type="note">
|
||||
You want to ask different follow up questions based on the rating? No problem with Logic Jumps!
|
||||
</Callout>
|
||||
|
||||
<Image
|
||||
src={NewsletterSurvey}
|
||||
alt="Formbricks newsletter survey editor"
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
3. When you are done customizing your survey questions, navigate to the **Settings** tab and choose the type of survey you want. Here, you have to pick Link Survey.
|
||||
|
||||
4. After configuring the survey settings however you like, click the **Publish** button on the top right corner of the page, and a modal should pop up once this is successful.
|
||||
|
||||
|
||||
## Embedding the survey in your email
|
||||
<Image
|
||||
src={NewsletterSurveyModal}
|
||||
alt="Formbricks newsletter survey modal"
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
Since you would traditionally want to embed this survey in your email, select the **Embed Survey** card. You will be directed to another window, where you'll find **Embed in Email**.
|
||||
|
||||
<Image
|
||||
src={NewsletterSurveyCode}
|
||||
alt="Formbricks newsletter survey embed code"
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
5. Finally, click the button with the “**View Embed Code**” text at the top right corner of the modal and simply copy the HTML code for your survey anywhere you want it in your newsletter. You can see the preview in the above image.
|
||||
|
||||
So, how does this work under the hood? In the next section, we’ll see how Formbricks utilizes link prefilling to create personalized responses for you.
|
||||
|
||||
## How to improve the analysis by identifying users
|
||||
|
||||
Apart from the [data prefilling](https://formbricks.com/docs/link-surveys/data-prefilling) needed to store which rating a user clicked on in an email, we use Formbricks link identification.
|
||||
|
||||
Link identification lets you link a response to a person. Whenever you set a `userId` in the URL, the Formbricks backend will create a new person profile.
|
||||
|
||||
This is what the link looks like:
|
||||
|
||||
`https://formbricks.com/s/clrgp68g2569g1225h3f5ayql?rating=5&userId=johannes@formbricks.com`
|
||||
|
||||
### When do I need identified users?
|
||||
|
||||
This is mostly useful if you know that you will collect more feedback from this one person. For example, if you run an in-app or website survey, you can also pass the userId to Formbricks and both responses will be attributed to the same user.
|
||||
|
||||
This obiously also works for surveys sent to the same user over the customer lifetime.
|
||||
|
||||
In your dashboard on Formbricks, here’s how it will look:
|
||||
|
||||
<Image
|
||||
src={SurveyResponses}
|
||||
alt="Formbricks personalized survey responses"
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
As Johannes completes the survey, you'll see a personalized, full response from him.
|
||||
|
||||
## How to use Hidden Fields to attach more info to responses
|
||||
|
||||
There might be more information you already have and want to use in the analysis of your survey results. A good way to facilitate that is using Hidden Fields.
|
||||
|
||||
Hidden Fields - as you can tell from the name - do not appear in the survey flow. They can be filled via URL as follows:
|
||||
|
||||
`?fieldId=Content`
|
||||
|
||||
So for example: `?job=Founder`. Or combinbed with the rating and identification parameters:
|
||||
|
||||
`https://formbricks.com/s/clrgp68g2569g1225h3f5ayql?rating=5&userId=johannes@formbricks.com&fieldId=Founder`
|
||||
|
||||
As you see, the survey URL is powerful ally when it comes to getting the most out of your survey respondents 😎
|
||||
|
||||
<Callout title="Auto-create surveys via API" type="note">
|
||||
If you send out a lot of emails periodically, it might be worth creating your surveys automatically. Formbricks has an API for it!
|
||||
|
||||
Going through that in detail would lead to far, but here is all the [documentation you need.](https://formbricks.com/docs/api/management/surveys#create-survey) We're also more than happy helping you set it up in our [Discord](https://formbricks.com/discord)
|
||||
</Callout>
|
||||
|
||||
## In Conclusion
|
||||
|
||||
Understanding what resonates with your readers is the compass that points toward growth, and so far, we’ve seen how Formbricks can help you achieve this. Formbricks not only provides actionable insights but also serves as a bridge, closing the gap between your content and reader satisfaction.
|
||||
|
||||
Formbricks is free and easy to get started with. All you have to do is [create an account](http://formbricks.com/) and you are good to go 🚀
|
||||
|
||||
Source for ROI figure:
|
||||
[Omnisend Blog](https://www.omnisend.com/blog/email-marketing-roi/#:~:text=What's%20an%20average%20email%20marketing,%2440%20for%20every%20dollar%20spent.)
|
||||
|
||||
export default ({ children }) => <LayoutMdx meta={meta}>{children}</LayoutMdx>;
|
||||
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 115 KiB |
@@ -1,11 +1,10 @@
|
||||
import AuthorBox from "@/components/shared/AuthorBox";
|
||||
import LayoutMdx from "@/components/shared/LayoutMdx";
|
||||
import Image from "next/image";
|
||||
|
||||
import HeaderImage from "./create-a-new-survey-with-formbricks.png";
|
||||
import LayoutMdx from "@/components/shared/LayoutMdx";
|
||||
import MonorepoImage from "./formbricks-monorepo-folder-structure.png";
|
||||
import PackagesFolderImage from "./formbricks-packages-folder.png";
|
||||
import HeaderImage from "./create-a-new-survey-with-formbricks.png";
|
||||
import GitpodImage from "./setup-formbricks-via-gitpod.png";
|
||||
import PackagesFolderImage from "./formbricks-packages-folder.png";
|
||||
import AuthorBox from "@/components/shared/AuthorBox";
|
||||
|
||||
export const meta = {
|
||||
title: "Join the FormTribe 🔥",
|
||||
@@ -17,7 +16,7 @@ export const meta = {
|
||||
tags: ["Open-Source", "No-Code", "Formbricks", "Geting started", "Welcome guide"],
|
||||
};
|
||||
|
||||
<AuthorBox name="Johannes" title="Co-Founder" date="October 1st, 2023" duration="4" author={"Johannes"} />
|
||||
<AuthorBox name="Johannes" title="Co-Founder" date="October 1st, 2023" duration="4" author={"Johannes"}/>
|
||||
|
||||
<Image src={HeaderImage} alt="Title Image" className="w-full rounded-lg" />
|
||||
|
||||
@@ -55,9 +54,9 @@ To get up and running we have 2 options: Gitpod and local.
|
||||
|
||||
With Gitpod you can run all of Formbricks in the cloud. With one click you can start coding right away in your browser:
|
||||
|
||||
<Image src={GitpodImage} alt="Setup Formbricks via Gitpod" className="w-full rounded-lg" />
|
||||
[](https://gitpod.io/#https://github.com/formbricks/formbricks)
|
||||
|
||||
[Read more in our docs](https://formbricks.com/docs/contributing/setup#gitpod-guide)
|
||||
<Image src={GitpodImage} alt="Setup Formbricks via Gitpod" className="w-full rounded-lg" />
|
||||
|
||||
#### Run on a local machine
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 31 KiB |
@@ -1,162 +0,0 @@
|
||||
import Image from "next/image";
|
||||
import AuthorBox from "@/components/shared/AuthorBox";
|
||||
import LayoutMdx from "@/components/shared/LayoutMdx";
|
||||
import Formbricks from './formbricks-dashboard.png';
|
||||
import Header from './cover-waitlist-survey-questions-open-source-free-tool copy.webp';
|
||||
import Waiting from './waitlist-survey-best-practices-waiting-for-submissions.webp';
|
||||
import Awareness from './awareness-urgency-waitlist-questions-survey-form.png';
|
||||
import Demographics from './demographics-contact-waitlist-survey-questions-for-more-leads.png';
|
||||
import Features from './features-wip-waitlist-form-questions-perfect-open-source.png';
|
||||
import Expectations from './pref-expectations-what-to-ask-waitlist-survey-form-open-source.png';
|
||||
import Testing from './testing-feedback-waitlist-questions-form-survey.png';
|
||||
|
||||
export const meta = {
|
||||
title: "The Perfect Waitlist Survey - What To Ask, Why, And How To Get The Most Out Of Your Leads",
|
||||
description:
|
||||
"Using a waitlist for product validation is tough. Use this waitlist best practice to gain clarity with actionable tips on questions to ask and insights utilization in this article.",
|
||||
date: "2024-01-23",
|
||||
publishedTime: "2022-01-23T11:52:00",
|
||||
authors: ["Olasunkanmi Balogun"],
|
||||
section: "Waitlist Survey",
|
||||
tags: ["Waitlist survey", "Waitlist Best Practice", "Waitlist Survey Questions"],
|
||||
};
|
||||
|
||||
<Image src={Header} alt="Waitlist Survey Questions asked right: Waitlist Best Practice" className="rounded-lg w-full" />
|
||||
|
||||
<AuthorBox
|
||||
name="Olasunkanmi Balogun"
|
||||
title="Content Writer"
|
||||
date="January 23rd, 2024"
|
||||
duration="8"
|
||||
author={"Ola"}
|
||||
/>
|
||||
|
||||
*Setting up a waitlist is easy; there are hundreds of tools for it. Using a waitlist as **a tool to validate your initial product and market hypothesis** is a lot harder: What do you ask? How much is too much? And what do you do with the insights?*
|
||||
|
||||
A waitlist can serve several purposes. Smart entrepreneurs recognize that they **don’t** **know** more than they **know** about the space they are starting to build in. Setting up a waitlist and generating some excitement around it is one of the cheapest and fastest ways to learn.
|
||||
|
||||
Fast and cheap is good, and waitlists are good. In its simplest form, a waitlist is just an email field and a sign-up button. However, this is almost always a wasted opportunity. Why?
|
||||
|
||||
When you manage to get someone onto your landing page and they are excited enough to trust you with their email address, they are willing to spend 30 more seconds and answer a few of your questions. But what if they don’t?
|
||||
|
||||
<Image src={Waiting} alt="Waitlist Best Practies Survey Questions" className="rounded-lg w-full" />
|
||||
|
||||
## Waitlist Friction Is Your Friend
|
||||
|
||||
“But what if they drop out because the waitlist survey is too long?” many worry. Worry not, because **they are doing you a favor.** Why?
|
||||
|
||||
Because if people are not willing to spend 30 seconds on your survey, they will never go through the effort to deal with your hopefully crappy MVP. But they will provide feedback, which might lead to taking your offering down the wrong path. Many people have opinions, but you should only listen to the voices of people who already have or very likely will buy from you. Friction can help you separate the wheat from the chaff.
|
||||
|
||||
## So, what does the perfect waitlist survey look like?
|
||||
|
||||
In this article, you’ll discover how to approach composing your list of questions for the waitlist survey. Your goal: You attract customers who actively participate on launch day.
|
||||
|
||||
Before going into the details, let's briefly explore why having an ideally crafted waitlist survey is essential.
|
||||
|
||||
### Why A Well-crafted Waitlist Is Crucial For Your Launch
|
||||
|
||||
A well-crafted waitlist survey contributes to the overall success of your product launch in several ways. As an example, if you have a new product and you are looking to create awareness, a well-crafted waitlist can benefit you in the following ways:
|
||||
|
||||
- It helps you **form a group of people who are genuinely interested in your product.** By collecting information like their email addresses, you can create a community of potential users who may become loyal customers.
|
||||
- It provides valuable insights into **customer needs and pains**, helping you tailor your offerings to meet their specific needs.
|
||||
- Lastly, a well-structured waitlist can be a **powerful marketing asset**, creating buzz and anticipation around your upcoming launch.
|
||||
|
||||
Now that you know why you need a waitlist, what information should you seek from your leads? In the next section, we’ll uncover the essential questions you should include in your waitlist survey.
|
||||
|
||||
## What To Ask On Your Waitlist Survey
|
||||
|
||||
Determining the right questions for your waitlist survey is essential and can vary based on your survey's purpose. Since the questions itself depend highly on the product or service you’re creating the waitlist for, this section provides examples and highlights key questions to gather valuable insights from your potential users.
|
||||
|
||||
<Image src={Awareness} alt="Waitlist Survey Questions to raise awareness about product launch with this waitlist best practice" className="rounded-lg w-full -mb-6 mt-12" />
|
||||
|
||||
### Questions About Problem Awareness & Urgency
|
||||
|
||||
This will help you probe the market's challenges, ask about any current products or services they use, what they like or dislike about them, how urgently they need a solution, and the impact of the problem on their daily lives or work. Some examples of questions you can ask in this survey include:
|
||||
|
||||
- **What specific challenges or problems related to [your industry/niche] are you currently experiencing?**
|
||||
- **On a scale of 1 to 10, how urgently do you feel the need to find a solution to these challenges?**
|
||||
- **Can you describe a situation where these challenges had a significant impact on your personal life/work/business?**
|
||||
|
||||
<Image src={Features} alt="Waitlist Survey Questions Features" className="rounded-lg w-full -mb-6 mt-12" />
|
||||
|
||||
### Question About Feature Preferences & Willingness to Pay
|
||||
|
||||
This question will help you inquire about the features they consider essential, how they want it built, and how much they are willing to invest in a solution. You can ask questions like:
|
||||
|
||||
- **Are there any features you would like to see in our [product/service]?**
|
||||
- **When would you find [product/service] too expensive?**
|
||||
- **Would you prefer a [product/service] with a lower upfront cost and additional charges for advanced features, or a higher upfront cost with all features included?**
|
||||
|
||||
<Image src={Testing} alt="Perfect Waitlist Survey Example Questions around Testing" className="rounded-lg w-full -mb-6 mt-12" />
|
||||
|
||||
### Willingness To Beta Test Or Provide Feedback
|
||||
|
||||
Asking this question will help you gauge their interest in trying out pre-launch versions and their willingness to provide constructive feedback. Examples of questions you can ask here include:
|
||||
|
||||
- **Would you be open to providing detailed feedback on your experience with our [product/service] during the beta testing phase?**
|
||||
- **Are you interested in being part of our exclusive beta testing group for [product/service]?**
|
||||
|
||||
<Image src={Expectations} alt="Waitlist Survey Questions Expectations" className="rounded-lg w-full -mb-6 mt-12" />
|
||||
|
||||
### Communication Preferences & Expectations
|
||||
|
||||
Ask how they prefer to be contacted and the level of engagement they expect from your end. Ask questions like the following:
|
||||
|
||||
- **How frequently would you like to receive updates about our [product/service]?**
|
||||
- **Through which channels do you prefer to receive updates and information about our [product/service]? (Then include options like: Email, SMS, In-app notifications, Social Media, and Others)**
|
||||
|
||||
<Image src={Demographics} alt="What to ask on Waitlist Survey Questions asked right to measure demographics" className="rounded-lg w-full -mb-6 mt-12" />
|
||||
|
||||
### Questions About Demographics And Contact Information
|
||||
|
||||
This question helps you gather details like age, gender, location, and contact information to build a profile of your potential users and a foundation for personalized engagement.
|
||||
|
||||
## Which tool to use? Go free & open-source
|
||||
|
||||
Building your waitlist survey has never been simpler or faster, especially now that you have a clear understanding of the questions and how to structure them. Rather than starting from scratch, leverage a tool that streamlines the process.
|
||||
|
||||
Enter Formbricks, an open-source survey solution designed to capture targeted user feedback precisely when it matters most in their journey. Utilizing the world's largest open-source survey stack, Formbricks empowers you to effortlessly create a personalized waitlist survey within minutes of signing up on the platform.
|
||||
|
||||
<Image
|
||||
src={Formbricks}
|
||||
alt="Formbricks is a free and open source survey software for in app micro surveys. Ask any user segment at any point in the user journey."
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
As you craft your survey, Formbricks provides the flexibility to incorporate logic jumps based on responses and include various survey types, such as multi-select, single-select, NPS, and rating surveys, among others.
|
||||
|
||||
Moreover, customization extends to the presentation of your survey. Whether you prefer a link-based survey or embedding it in your app, Formbricks caters to your needs. For instance, if you have a waitlist landing page, you can easily embed the survey by installing the Formbricks widget. Implementing a survey trigger is equally straightforward, whether based on the inner text of a button (e.g., a "Join our waitlist" button on your landing page), the page URL of your landing page, or CSS selector. Formbricks puts the power of efficient, tailored survey creation at your fingertips.
|
||||
|
||||
As an illustration, we've crafted a sample link waitlist survey featuring a blend of questions from the provided examples. Check it out [here](https://app.formbricks.com/s/clrf1zsp41ijp1225vt5n022q).
|
||||
|
||||
However, it is not enough to just have a waitlist and know how to build it; you need to take actionable steps to make the most of the leads you have amassed. In the next section, we’ll explore a few ideas to convert your subscribers into actual users when you launch.
|
||||
|
||||
## How To Get The Most Out Of Your Leads
|
||||
|
||||
The ideas in this section will guide you in extracting the full potential of your leads.
|
||||
|
||||
### Boost lead conversion with incentives
|
||||
|
||||
Encourage engagement by offering incentives that resonate with your audience. Whether it's exclusive access, discounts, special perks, or even gift cards, providing something extra can motivate your leads to convert into active users.
|
||||
|
||||
### Utilize FOMO
|
||||
|
||||
Create a sense of urgency and anticipation. Highlight the unique aspects of your launch and the benefits users will enjoy. By emphasizing exclusivity and limited-time offers, you can fuel FOMO, driving more conversions from your waitlist.
|
||||
|
||||
### Optimizing Conversion Rates through Timely Launches
|
||||
|
||||
Understanding the crucial role of timing in converting waitlist subscribers to active users is key. The data underscores this importance, revealing that for waitlists active for less than a month, conversion rates hover around 50%. Contrastingly, when a waitlist extends beyond three months, the conversion rate significantly drops below 20%. This emphasizes the need for a strategic and timely launch to maximize engagement and capitalize on the initial interest of your audience.
|
||||
|
||||
### Nurture Engagement through Consistent Interaction
|
||||
|
||||
Regular and proactive communication is the key to fostering a lasting relationship. Don't reserve updates solely for the launch day; instead, provide ongoing insights into your progress.
|
||||
|
||||
Consider sending out regular monthly updates encompassing new features, noteworthy news, engaging content, or any other relevant information. This consistent stream of information keeps your audience informed and invested in your journey.
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
So far, we've tackled the "whys" and "whats" of waitlist surveys—their benefits, key questions, and even how to build one easily with Formbricks.
|
||||
|
||||
Concluding the article, we explored four ideas to get the most out of your leads. But remember, while crafting your survey, make sure you keep it short and direct. According to SurveyMonkey, 45% of respondents express comfort with surveys that take up to 5 minutes of their time. As a result, when crafting your survey, prioritize conciseness and directness. Ensure your messaging acknowledges the respondent's valuable time and expresses gratitude for their willingness to participate.
|
||||
|
||||
export default ({ children }) => <LayoutMdx meta={meta}>{children}</LayoutMdx>;
|
||||
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 189 KiB |
|
Before Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 241 KiB |
@@ -1,442 +0,0 @@
|
||||
import AuthorBox from "@/components/shared/AuthorBox";
|
||||
import LayoutMdx from "@/components/shared/LayoutMdx";
|
||||
import Image from "next/image";
|
||||
|
||||
import Header from "./header.webp";
|
||||
import Container from "./container.webp";
|
||||
import Docker from "./docker.webp";
|
||||
import DockerCompose from "./docker-compose.webp";
|
||||
import SmartestPersonGif from "./smartest-person.gif";
|
||||
import SelfHostGif from "./self-host.gif";
|
||||
import SuperSonicGif from "./supersonic.gif";
|
||||
import EndingGif from "./ending.gif";
|
||||
|
||||
export const meta = {
|
||||
title: "Understand Formbricks Self Hosting",
|
||||
description:"We explain how we internally built Formbricks self hosting architecture with Docker, Docker Compose, and Bash Script. It's easy and to-the-point!",
|
||||
date: "2024-01-23",
|
||||
publishedTime: "2024-01-23T12:00:00",
|
||||
authors: ["Shubham Palriwala"],
|
||||
section: "Infrastructure",
|
||||
tags: ["Infrastructure", "Docker", "Docker Compose", "Bash Script", "Self Hosting"],
|
||||
};
|
||||
|
||||
<Image src={Header} alt="How to effectively self host Formbricks" className="w-full rounded-lg" />
|
||||
|
||||
<AuthorBox
|
||||
name="Shubham Palriwala"
|
||||
title="Open Source Engineer"
|
||||
date="January 23rd, 2024"
|
||||
duration="20"
|
||||
author={"Shubham"}
|
||||
/>
|
||||
|
||||
_Embracing the open-source ethos of sharing knowledge, we're unveiling the inner workings of self-hosting a Formbricks instance. This initiative is designed to share & learn with [our community](https://formbricks.com/discord), providing deep insights into Formbricks' features for all, from developers to end-users._
|
||||
|
||||
## What is Self Hosting?
|
||||
|
||||
Self-hosting allows you to manage our Formbricks app **on your own server infrastructure**, providing complete control over its environment. This contrasts with relying on external cloud services. At Formbricks, we facilitate a swift and straightforward self-hosting process, enabling quick setup of your instance. For those who prefer a quicker way to get things going rather than managing it on your own, our cloud-based solution is readily accessible at [app.formbricks.com](https://app.formbricks.com).
|
||||
|
||||
<Image
|
||||
src={SelfHostGif}
|
||||
alt="Self Host Formbricks with ease with our Docker, Docker Compose and Bash Script setup on your own Infrastructure"
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
### When should one self-host?
|
||||
|
||||
Deciding to self-host is particularly advantageous in certain scenarios:
|
||||
|
||||
1. **Deeper Customizations:** Self-hosting provides the flexibility to modify and tailor the application according to specific needs, which is crucial for bespoke workflows and functionalities.
|
||||
2. **Hosting on Internal Enterprise Networks:** For organizations that prioritize data security and operational control, self-hosting on their own networks ensures that sensitive data and resources remain within their secure perimeter.
|
||||
3. **Data Sovereignty:** When there's a need to keep data in-house for privacy, security, or regulatory reasons, self-hosting is the ideal solution.
|
||||
4. **Policy Compliance:** Certain industries are governed by strict policies and regulations that mandate how and where data is stored and processed. Self-hosting enables adherence to these specific compliance requirements.
|
||||
|
||||
In these situations, self-hosting offers an unmatched level of control and customization.
|
||||
|
||||
|
||||
### What to expect from this guide?
|
||||
|
||||
Embark on an insightful journey into the world of Formbricks self-hosting. We'll dive deep into two key methods: the [Advanced Docker Setup](https://formbricks.com/docs/self-hosting/docker) and the [Single Script Setup](https://formbricks.com/docs/self-hosting/production). Along the way, we'll demystify several core concepts:
|
||||
|
||||
1. **Containerization:** Discover how software is encapsulated in containers, a crucial step for efficient deployment and scaling, and fundamental to understanding Formbricks in various environments.
|
||||
2. **Docker:** Explore Docker, a vital element of our containerization approach, and learn how it streamlines the creation, deployment, and management of applications.
|
||||
3. **Docker Compose:** Understand the role of Docker Compose in managing multi-container Docker applications, enhancing the orchestration and deployment process.
|
||||
4. **Advanced Setup Breakdown:** Delve into the details of our Advanced Setup method, examining the intricacies of Dockerfile and Docker-Compose files & apply the concepts learned in the previous step.
|
||||
5. **Server Concepts:** Grasp essential concepts like Proxies, Reverse Proxies, SSL, and Cronjobs, key to comprehending Formbricks' server interactions.
|
||||
6. **Single Shell Script Breakdown:** Conclude with an in-depth analysis of the Formbricks self-hosting script, illustrating how these concepts unite to forge a powerful, self-hosted Formbricks environment.
|
||||
|
||||
Each of these topics is carefully chosen to give you a holistic understanding of the technical foundations of Formbricks, allowing you to leverage its full potential, whether you're a developer, administrator, or end-user.
|
||||
|
||||
### 1. What is Containerisation
|
||||
|
||||
Containerisation involves packaging all your project's dependencies and essentials into a single, portable unit.
|
||||
|
||||
<Image
|
||||
src={Container}
|
||||
alt="Containerization is the process of packaging software code, its dependencies, configurations, and other essentials into a single, portable unit."
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
Think of software containerisation as placing your application in a compact, self-sufficient unit. Like a standardized box that's indifferent to the truck carrying it or the journey it takes, each container focuses solely on preserving its contents — the application and its dependencies. This ensures the application operates reliably, no matter the underlying infrastructure it’s deployed on.
|
||||
|
||||
If you want to dive deep into the internal workings of a container runtime, do check this really [cool talk](https://youtu.be/sK5i-N34im8?feature=shared) from Jérôme Petazzoni on Youtube!
|
||||
|
||||
### 2. What is Docker
|
||||
|
||||
Docker is a container runtime that enables you to abstract your projects into resharable instances. In the context of our earlier analogy, if containerization is about securely packing your application (like goods in a container), Docker is the truck that carries these containers.
|
||||
|
||||
<Image
|
||||
src={Docker}
|
||||
alt="Docker is a container runtime that enables you to abstract your projects into resharable instances."
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
Building on the earlier analogy, Docker can be seen as the fleet of trucks that transport these secure boxes (containers). Each truck (Docker) is equipped to carry a container, ensuring it reaches its destination intact and operates smoothly. Docker takes care of the driving, navigating through different environments (operating systems), and making sure each container is running as it should, regardless of where it's deployed.
|
||||
|
||||
<Note>
|
||||
It's important to note that Docker is a **runtime environment** — it's responsible for running the containers, rather than being a technology that creates them. While Docker is widely used, there are other alternatives in the field, such as Podman and Containerd, which also offer container runtime capabilities.
|
||||
</Note>
|
||||
|
||||
### Understanding Formbricks Dockerfile
|
||||
|
||||
The Dockerfile for our Formbricks application is structured into two key stages - the Builder and Runner stages. It’s currently hosted [here](https://github.com/formbricks/formbricks/blob/main/apps/web/Dockerfile).
|
||||
|
||||
<CodeGroup title="Dockerfile">
|
||||
|
||||
```{{ title: 'Builder Stage' }}
|
||||
# Installer stage: Building the application
|
||||
FROM node:20-alpine AS installer
|
||||
|
||||
# Enable corepack and prepare pnpm
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
|
||||
# Install necessary build tools and compilers
|
||||
RUN apk update && apk add --no-cache g++ cmake make gcc python3 openssl-dev
|
||||
|
||||
# Install Supercronic (cron for containers without super user privileges)
|
||||
RUN apk add --no-cache curl \
|
||||
&& curl -fsSLo /tmp/supercronic \
|
||||
"https://github.com/aptible/supercronic/releases/download/v0.2.27/supercronic-linux-amd64" \
|
||||
&& chmod +x /tmp/supercronic
|
||||
|
||||
# Set environment variables
|
||||
ARG DATABASE_URL
|
||||
ENV DATABASE_URL=$DATABASE_URL
|
||||
|
||||
ARG NEXTAUTH_SECRET
|
||||
ENV NEXTAUTH_SECRET=$NEXTAUTH_SECRET
|
||||
|
||||
ARG ENCRYPTION_KEY
|
||||
ENV ENCRYPTION_KEY=$ENCRYPTION_KEY
|
||||
|
||||
# Set the working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the application files
|
||||
COPY . .
|
||||
|
||||
# Create a .env file
|
||||
RUN touch /app/apps/web/.env
|
||||
|
||||
# Install the dependencies
|
||||
RUN pnpm install
|
||||
|
||||
|
||||
# Build the project
|
||||
RUN pnpm post-install --filter=web...
|
||||
RUN pnpm turbo run build --filter=web...
|
||||
```
|
||||
|
||||
```{{ title: 'Runner Stage' }}
|
||||
# Runner stage: Setting up the runtime environment
|
||||
FROM node:20-alpine AS runner
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
|
||||
RUN apk add --no-cache curl \
|
||||
# && addgroup --system --gid 1001 nodejs \
|
||||
&& adduser --system --uid 1001 nextjs
|
||||
|
||||
WORKDIR /home/nextjs
|
||||
|
||||
COPY --from=installer /tmp/supercronic /usr/local/bin/supercronic
|
||||
COPY --from=installer /app/apps/web/next.config.mjs .
|
||||
COPY --from=installer /app/apps/web/package.json .
|
||||
# Leverage output traces to reduce image size
|
||||
COPY --from=installer --chown=nextjs:nextjs /app/apps/web/.next/standalone ./
|
||||
COPY --from=installer --chown=nextjs:nextjs /app/apps/web/.next/static ./apps/web/.next/static
|
||||
COPY --from=installer --chown=nextjs:nextjs /app/apps/web/public ./apps/web/public
|
||||
COPY --from=installer --chown=nextjs:nextjs /app/packages/database/schema.prisma ./packages/database/schema.prisma
|
||||
COPY --from=installer --chown=nextjs:nextjs /app/packages/database/migrations ./packages/database/migrations
|
||||
COPY --from=installer /app/docker/cronjobs /app/docker/cronjobs
|
||||
|
||||
EXPOSE 3000
|
||||
ENV HOSTNAME "0.0.0.0"
|
||||
USER nextjs
|
||||
|
||||
# Prepare volume for uploads
|
||||
RUN mkdir -p /home/nextjs/apps/web/uploads/
|
||||
VOLUME /home/nextjs/apps/web/uploads/
|
||||
|
||||
CMD supercronic -quiet /app/docker/cronjobs & \
|
||||
if [ "$NEXTAUTH_SECRET" != "RANDOM_STRING" ]; then \
|
||||
pnpm dlx prisma migrate deploy && \
|
||||
exec node apps/web/server.js; \
|
||||
else \
|
||||
echo "ERROR: Please set a value for NEXTAUTH_SECRET in your docker compose variables!" >&2; \
|
||||
exit 1; \
|
||||
fi
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
**Builder Stage:**
|
||||
|
||||
1. **Base Image:** Uses **`node:20-alpine`**, a lightweight Node.js environment as a base.
|
||||
2. **Setup:** Installs **`corepack`** and **`pnpm`** for package management, along with necessary build tools.
|
||||
3. **Supercronic Installation:** Adds Supercronic for cron job management.
|
||||
4. **Environment Prep:** Sets up essential environment variables.
|
||||
5. **File Copy and Build:** Transfers application files, installs dependencies, compiles & builds the Formbricks app.
|
||||
|
||||
**Runner Stage:**
|
||||
|
||||
1. **Base Image and Setup:** Similar to the builder stage, but for the runtime environment.
|
||||
2. **User Configuration:** Creates a non-root **`nextjs`** user for security.
|
||||
3. **File Transfer:** Copies only necessary build files from the builder stage.
|
||||
4. **Port and Volume:** Exposes port 3000 and sets up a persistent volume mapping for uploads.
|
||||
5. **Application Launch:** Uses a command sequence to run
|
||||
|
||||
|
||||
### 3. What is Docker Compose
|
||||
|
||||
Docker Compose is a tool designed to manage multiple containers, streamlining the process of running multi-container Docker applications. It allows for the configuration of how these containers interact, including mapping ports, creating, and sharing data volumes. This orchestration is crucial when your application comprises several interconnected containers, each with its specific role.
|
||||
|
||||
<Image
|
||||
src={DockerCompose}
|
||||
alt="Docker Compose is a tool designed to manage multiple containers, streamlining the process of running multi-container Docker applications."
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
Extending our base analogy to Docker Compose, think of it as the logistics coordinator for a fleet of trucks (Docker). Docker Compose manages the entire fleet, directing which truck goes where, how they interact, and ensuring they are all synchronized in their operations. It's like planning the routes for multiple trucks, deciding which container gets loaded onto which truck, and how they share the road (network and data volumes). Docker Compose simplifies the complexity of coordinating multiple containers (trucks), ensuring they work together harmoniously as a unified system.
|
||||
|
||||
### 4. Advanced Setup Breakdown: Understanding Formbricks Docker Compose
|
||||
|
||||
<CodeGroup title="Docker Compose">
|
||||
|
||||
```yml {{ title: 'docker-compose.yml' }}
|
||||
version: "3.3"
|
||||
x-environment: &environment
|
||||
environment:
|
||||
# The url of your Formbricks instance used in the admin panel
|
||||
WEBAPP_URL:
|
||||
|
||||
# PostgreSQL DB for Formbricks to connect to
|
||||
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/formbricks?schema=public"
|
||||
|
||||
# NextJS Auth
|
||||
# @see: https://next-auth.js.org/configuration/options#nextauth_secret
|
||||
# You can use: `openssl rand -hex 32` to generate one
|
||||
NEXTAUTH_SECRET:
|
||||
|
||||
# Set this to your public-facing URL, e.g., https://example.com
|
||||
# You do not need the NEXTAUTH_URL environment variable in Vercel.
|
||||
NEXTAUTH_URL: http://localhost:3000
|
||||
|
||||
# Encryption Key is used for 2FA & Single use URLs for Link Surveys
|
||||
# You can use: $(openssl rand -hex 32) to generate one
|
||||
ENCRYPTION_KEY:
|
||||
|
||||
# PostgreSQL password
|
||||
POSTGRES_PASSWORD: postgres
|
||||
|
||||
# Enterprise License Key
|
||||
# Required to access Enterprise-only features
|
||||
# ENTERPRISE_LICENSE_KEY:
|
||||
|
||||
# Email Configuration
|
||||
# MAIL_FROM:
|
||||
# SMTP_HOST:
|
||||
# SMTP_PORT:
|
||||
# SMTP_SECURE_ENABLED:
|
||||
# SMTP_USER:
|
||||
# SMTP_PASSWORD:
|
||||
|
||||
# Set the below value if you have and want to use a custom URL for the links created by the Link Shortener
|
||||
# SHORT_URL_BASE:
|
||||
|
||||
# Set the below to 0 to enable Email Verification for new signups (will required Email Configuration)
|
||||
EMAIL_VERIFICATION_DISABLED: 1
|
||||
|
||||
# Set the below to 0 to enable Password Reset (will required Email Configuration)
|
||||
PASSWORD_RESET_DISABLED: 1
|
||||
|
||||
# Uncomment the below and set it to 1 to disable Signups
|
||||
# SIGNUP_DISABLED:
|
||||
|
||||
# Uncomment the below and set it to 1 to disable Invites
|
||||
# INVITE_DISABLED:
|
||||
|
||||
# Uncomment the below and set a value to have your own Privacy Page URL on the signup & login page
|
||||
# PRIVACY_URL:
|
||||
|
||||
# Uncomment the below and set a value to have your own Terms Page URL on the auth and the surveys page
|
||||
# TERMS_URL:
|
||||
|
||||
# Uncomment the below and set a value to have your own Imprint Page URL on the auth and the surveys page
|
||||
# IMPRINT_URL:
|
||||
|
||||
# Uncomment the below and set to 1 if you want to enable GitHub OAuth
|
||||
# GITHUB_AUTH_ENABLED:
|
||||
# GITHUB_ID:
|
||||
# GITHUB_SECRET:
|
||||
|
||||
# Uncomment the below and set to 1 if you want to enable Google OAuth
|
||||
# GOOGLE_AUTH_ENABLED:
|
||||
# GOOGLE_CLIENT_ID:
|
||||
# GOOGLE_CLIENT_SECRET:
|
||||
|
||||
# Uncomment the below to automatically assign new users to a specific team and role within that team
|
||||
# Insert an existing team id or generate a valid CUID for a new one at https://www.getuniqueid.com/cuid (e.g. cjld2cjxh0000qzrmn831i7rn)
|
||||
# (Role Management is an Enterprise feature)
|
||||
# DEFAULT_TEAM_ID:
|
||||
# DEFAULT_TEAM_ROLE: admin
|
||||
|
||||
# Uncomment and set to 1 to skip onboarding for new users
|
||||
# ONBOARDING_DISABLED: 1
|
||||
|
||||
services:
|
||||
postgres:
|
||||
restart: always
|
||||
image: postgres:15-alpine
|
||||
volumes:
|
||||
- postgres:/var/lib/postgresql/data
|
||||
<<: *environment
|
||||
|
||||
formbricks:
|
||||
restart: always
|
||||
image: ghcr.io/formbricks/formbricks:latest
|
||||
depends_on:
|
||||
- postgres
|
||||
ports:
|
||||
- 3000:3000
|
||||
volumes:
|
||||
- uploads:/home/nextjs/apps/web/uploads/
|
||||
<<: *environment
|
||||
|
||||
volumes:
|
||||
postgres:
|
||||
driver: local
|
||||
uploads:
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
- Version Specification:
|
||||
- **`version: "3.3"`**: Specifies the Docker Compose file version, which determines the syntax and functionalities available.
|
||||
- Environment Variables Setup (x-environment):
|
||||
- **`x-environment: &environment`**: A reusable anchor for environment variables, detailing settings for the Formbricks instance, database connections, authentication, and optional configurations like email, OAuth, and URLs for terms/privacy.
|
||||
- Services Configuration:
|
||||
1. **Postgres Service**:
|
||||
- **`postgres:`**: Defines the PostgreSQL service.
|
||||
- **`restart: always`**: Ensures the container restarts automatically if it stops.
|
||||
- **`image: postgres:15-alpine`**: Uses the Postgres 15 image on Alpine Linux, a lightweight version.
|
||||
- **`volumes: - postgres:/var/lib/postgresql/data`**: Maps a named volume **`postgres`** to persist database data.
|
||||
- **`<<: *environment`**: Inherits the environment settings defined earlier.
|
||||
2. **Formbricks Service**:
|
||||
- **`formbricks:`**: Defines the Formbricks application service.
|
||||
- **`restart: always`**: Similar to Postgres, configures the container to restart automatically.
|
||||
- **`image: ghcr.io/formbricks/formbricks:latest`**: Pulls the latest Formbricks image from GitHub Container Registry.
|
||||
- **`depends_on: - postgres`**: Specifies that Formbricks depends on the PostgreSQL service.
|
||||
- **`ports: - 3000:3000`**: Maps port 3000 from the container to the host, allowing web access to the application.
|
||||
- **`volumes: - uploads:/home/nextjs/apps/web/uploads/`**: Sets a volume for uploads.
|
||||
- **`<<: *environment`**: Inherits environment settings.
|
||||
- Volumes Definition:
|
||||
- **`volumes:`**
|
||||
- **`postgres: { driver: local }`**: Defines a local volume for PostgreSQL data, ensuring data persistence.
|
||||
- **`uploads:`**: Sets up a volume for storing uploads in the Formbricks application.
|
||||
|
||||
This Docker Compose file orchestrates the Formbricks application and its database, providing a harmonized and efficient deployment setup. It highlights the ease of configuring and running a multi-container application, where each service is finely tuned and interconnected.
|
||||
|
||||
<Image
|
||||
src={SmartestPersonGif}
|
||||
alt="Explained Formbricks Docker Compose line by line"
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
|
||||
That’s it! **You’ve understood our Advanced Docker Setup** in the Self Hosting Stack! Congratulations! Now lets go a step further and understand our cool Single Script Setup too!
|
||||
|
||||
|
||||
### Quick Server Concepts before we understand our Single Script Setup
|
||||
|
||||
1. **Proxy**:
|
||||
A proxy server acts as an intermediary between a user's computer and the internet. It's used to request resources from other servers, offering benefits like improved security and performance, and controlled access.
|
||||
2. **Reverse Proxy**:
|
||||
A reverse proxy sits in front of web servers and forwards client requests to those web servers. It's key for load balancing, providing SSL termination, and ensuring secure and anonymous browsing.
|
||||
3. **SSL (Secure Sockets Layer)**:
|
||||
SSL is a standard security technology for establishing an encrypted link between a web server and a browser. It ensures that all data passed between the web server and browsers remain private and integral, a must-have for securing online transactions.
|
||||
4. **Cronjobs**:
|
||||
Cronjobs are scheduled tasks that automate scripts at specified times. They're crucial for routine tasks like backups and system updates. However, managing cronjobs in Docker can be challenging due to its isolated environment.
|
||||
|
||||
### Struggles with running Cronjobs in Docker env
|
||||
|
||||
Cronjobs are typically straightforward to write and administer, but we encountered challenges when trying to run them inside our Docker container without root permissions and with a limited view of system environment variables. This issue is common in Docker's isolated environment, where running containers as root is considered a security risk.
|
||||
|
||||
<Image
|
||||
src={SuperSonicGif}
|
||||
alt="Formbricks uses Supercronic to run cronjobs in Docker without root permissions and with a limited view of system environment variables."
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
After much deliberation and exploring various workarounds, we discovered [Supercronic](https://github.com/aptible/supercronic). This open-source job scheduler is tailor-made for Docker, providing a reliable solution for executing cronjobs without needing superuser privileges. Additionally, it effectively accesses the full environment context, making it an ideal fit for our setup.
|
||||
|
||||
### Understanding our shell script:
|
||||
|
||||
This shell script is designed to streamline the setup and management of a Formbricks instance using Docker. It’s hosted [here](https://github.com/formbricks/formbricks/blob/main/docker/production.sh) currently for you to take a look. It's divided into various functions, each handling a specific aspect of the setup or maintenance process:
|
||||
|
||||
**Initial Setup**:
|
||||
|
||||
- The script begins with a shebang line (**`#!/bin/env bash`**) specifying that it should run in a Bash environment.
|
||||
- **`set -e`** ensures the script exits if any command fails.
|
||||
- The script then determines the Ubuntu version for contextual messages.
|
||||
|
||||
**Function: install_formbricks**:
|
||||
|
||||
- **Welcome Message**: Begins with a friendly greeting and info about the setup process.
|
||||
- **Remove Old Docker Installations**: Safely removes any existing Docker installations.
|
||||
- **Install Dependencies**: Updates package lists and installs necessary dependencies.
|
||||
- **Docker Setup**: Adds Docker's GPG key and sets up its stable repository, followed by installing Docker and its related components.
|
||||
- **Docker Installation Test**: Verifies Docker installation.
|
||||
- **User Configuration**: Adds the current user to the Docker group to avoid using sudo for Docker commands.
|
||||
- **Traefik Configuration**: Sets up Traefik for reverse proxy and SSL with user-provided email and domain name.
|
||||
- **Optional Email Service Setup**: If the user opts in, gathers SMTP details for email services.
|
||||
- **Download and Configure docker-compose.yml**: Downloads the Formbricks Docker Compose file and modifies it with custom settings.
|
||||
- **Final Setup Steps**: Creates a new group, brings up Docker containers, and provides final instructions.
|
||||
|
||||
**Function: uninstall_formbricks**:
|
||||
|
||||
- Prompts for confirmation and, if affirmed, stops and removes the Formbricks Docker containers and associated data.
|
||||
|
||||
**Additional Utility Functions**:
|
||||
|
||||
- **stop_formbricks**: Stops the running Formbricks instance.
|
||||
- **update_formbricks**: Pulls the latest Docker images and updates the Formbricks instance.
|
||||
- **restart_formbricks**: Restarts the Formbricks containers.
|
||||
|
||||
**Control Flow**:
|
||||
|
||||
- The script ends with a case statement allowing users to call specific functions based on the command-line argument `install, update, stop, restart, uninstall`
|
||||
|
||||
This script encapsulates all necessary steps for a user-friendly setup and maintenance of Formbricks, making it easy for users to deploy and manage their self-hosted Formbricks instance.
|
||||
|
||||
<Image
|
||||
src={EndingGif}
|
||||
alt="Explained Formbricks Self Hosting Setup in an easy to understand way"
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
|
||||
That’s it amigo! If you’re reading this, we hope you’ve understood our Self Hosting stack inside out now! Thank you for taking the time to read through this comprehensive guide on Formbricks self-hosting. Don't forget to check out the [Slides](https://pitch.com/v/understanding-Formbricks-e2e-tests-i54auf) for this session.
|
||||
|
||||
Your participation and feedback are vital in shaping our community and its resources. So, [join us on Discord](https://formbricks.com/discord), to be a part of the next session, engage in lively discussions, connect with fellow enthusiasts and Formbricks team!
|
||||
|
||||
export default ({ children }) => <LayoutMdx meta={meta}>{children}</LayoutMdx>;
|
||||
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 387 KiB |
|
Before Width: | Height: | Size: 7.0 MiB |