A Next.js application that integrates the Zendesk Web Widget messaging interface for automated customer support. The application provides a consent form, embeds the Zendesk widget, and logs user interactions via anonymous pixels.
This application serves as a support ticket deflection tool that:
- Displays a consent form before loading the Zendesk script and widget
- Renders the Zendesk messaging widget in embedded mode
- Customizes the widget with DuckDuckGo branding and theme colors
- Logs interactions via anonymous pixel events (clicks, messages, link navigation)
- Swaps article links to point to DuckDuckGo help pages instead of Zendesk articles
User visits page
↓
Consent form displayed
↓
User clicks the consent button
↓
Zendesk script loads
↓
Widget renders in embedded mode
↓
Hooks initialize (link swapping, click handlers, styles)
↓
User interacts with widget
↓
Events anonymously logged via pixels.js
app/page.tsx- Main page component managing widget lifecycle and event handlerscomponents/consent-form/- Privacy consent form shown before widget loadscomponents/footer/- Site footer with links and company information
The application uses three main hooks for Zendesk integration (see src/hooks/README.md for details):
useZendeskSwapArticleLinks- Replaces Zendesk article URLs with DuckDuckGo help page URLsuseZendeskClickHandlers- Attaches click handlers to buttons and links for anonymous event logginguseZendeskIframeStyles- Injects custom CSS styles into the widget iframe
utils/zendesk-iframe.ts- Functions to access the Zendesk messaging widget iframe and its documentutils/zendesk-observer.ts- Sets up MutationObserver on the Zendesk iframe for DOM change detectionutils/build-article-url.ts- Builds complete article URLs using the URL constructorutils/update-article-links.ts- Updates article links in a document based on article ID mappingutils/get-css-variable.ts- Reads CSS variable values from the document rootutils/get-slug-from-url.ts- Extracts and sanitizes the last path segment from a URL for use in pixel event tracking
The application uses a custom pixels.js script (public/scripts/pixels.js) for anonymous logging of events. No PII or device fingerprinting.
- Page impression - Fired when page loads (via
PageLoadPixelcomponent) - Button clicks - logs button interactions (send button, Yes/No buttons)
- Link clicks - Logs knowledge base article link clicks
Pixel configuration can be set via window.PIXEL_CONFIG before the script loads:
window.PIXEL_CONFIG = {
baseUrl: 'https://improving.duckduckgo.com/t/',
eventPrefix: 'subscriptionsupport_',
disableDeduplication: false, // Set to true to allow duplicate pixels
...
};- Page impression -
subscriptionsupport_impression- The first time user lands on the page - User consent -
subscriptionsupport_consent- User provides consent to privacy policy / TOU - First message -
subscriptionsupport_message_first- The first question / message per session that the user submits - Convert to ticket -
subscriptionsupport_link_ticket- When the user clicks the "Support Form" link to create a ticket - Error -
subscriptionsupport_jsexception- JavaScript Error object - Yes / No clicks:
subscriptionsupport_helpful_yesorsubscriptionsupport_helpful_no- User clicked either "Yes" or "No" button when asked "Was this helpful?" - Article link clicks with slug -
subscriptionsupport_helplink_$slug- User clicked a help page link (provided by the chat bot). Example:subscriptionsupport_helplink_getting-started(The DDG help page slug)
- Node.js (version specified in
.nvmrc) - npm or compatible package manager
npm installnpm run devFor HTTPS development (useful for testing Zendesk widget):
npm run dev:tlsOpen http://localhost:3000 (or https://localhost:3000 for TLS) to view the application.
npm run build
npm startCommon site configuration is in src/config/common.ts:
MAIN_SITE_URL- Main DuckDuckGo site URLSITE_TITLE- Application titleHELP_PAGES_BASE_URL- Base URL for help pages
Zendesk configuration is in src/config/zendesk.ts:
WEB_WIDGET_KEY- Zendesk Web Widget keyZENDESK_SCRIPT_URL- Zendesk script URLARTICLE_LINK_MAP- Mapping of Zendesk article IDs to help page paths
src/
├── app/ # Next.js App Router pages
│ ├── layout.tsx # Root layout with header, footer, theme provider
│ └── page.tsx # Main page with Zendesk integration
├── components/ # React components
│ ├── button/ # Reusable button component
│ ├── consent-form/ # Privacy consent form
│ ├── footer/ # Site footer
│ └── page-load-pixel/ # Page load event dispatcher
├── config/ # Configuration constants
│ ├── common.ts # Common site configuration
│ ├── fonts.ts # Font configuration
│ └── zendesk.ts # Zendesk widget configuration
├── constants/ # Application constants
│ ├── breakpoints.ts # Responsive breakpoint constants
│ ├── footerLinks.ts # Footer link definitions
│ ├── zendesk-selectors.ts # CSS selectors for Zendesk elements
│ ├── zendesk-styles.ts # Custom CSS for Zendesk iframe
│ └── zendesk-timing.ts # Timing constants for retries/delays
├── hooks/ # React hooks
│ ├── README.md # Documentation for Zendesk integration hooks
│ ├── use-media-query.ts # Responsive design hook
│ ├── use-zendesk-click-handlers.ts # Click event handlers
│ ├── use-zendesk-iframe-styles.ts # Style injection
│ └── use-zendesk-swap-article-links.ts # Article link swapping
├── reducers/ # State reducers
│ └── widget-reducer.ts # Widget lifecycle state reducer
├── tests/ # Test files
│ ├── fixtures/ # Test fixtures and mocks
│ │ └── zendesk-mock.js # Zendesk widget mock for testing
│ ├── integration/ # Integration tests
│ │ └── complete-flow.test.ts # End-to-end user flow tests
│ ├── unit/ # Unit tests
│ │ ├── build-article-url.test.ts # URL building utility tests
│ │ └── get-slug-from-url.test.ts # URL slug extraction tests
│ └── README.md # Testing documentation
├── types/ # TypeScript type definitions
│ └── zendesk.d.ts # Extended Zendesk Web Widget types
└── utils/ # Utility functions
├── build-article-url.ts # URL building utility
├── get-css-variable.ts # CSS variable reader
├── get-slug-from-url.ts # URL slug extraction and sanitization
├── update-article-links.ts # Link updating utility
├── zendesk-iframe.ts # Iframe access utilities
└── zendesk-observer.ts # MutationObserver setup utility
This application is configured to deploy to GitHub Pages via GitHub Actions.
The build process uses the CUSTOM_DOMAIN environment variable to determine if the app is being deployed to a custom domain. Set this variable in your GitHub repository:
- Go to your GitHub repository
- Click on Settings (top navigation)
- In the left sidebar, expand Secrets and variables
- Click on Actions
- Click on the Variables tab (not Secrets)
- Click New repository variable
- Set:
- Name:
CUSTOM_DOMAIN - Value:
true(if using a custom domain) orfalse(if using github.io URL)
- Name:
- Click Add variable
The workflow automatically runs on pushes to the main branch, but you can also trigger it manually:
- Go to your GitHub repository
- Click on the Actions tab
- In the left sidebar, click on Deploy
- On the right side, click the Run workflow button
- Select the branch (typically
main) - Click the green Run workflow button
The deployment will build the application, run tests, and deploy to GitHub Pages.