Skills
Skill 33 of 49
Integrate LaunchDarkly qualitative user feedback into a JavaScript/TypeScript codebase.
14 minutes · 3,119 words · 24 sections
Install
npx skills add launchdarkly/ai-tooling --skill launchdarkly-flag-qualitative-feedback-setupnpx skills add launchdarkly/ai-tooling/plugin marketplace add launchdarkly/ai-toolingThe first command installs just this skill, by the name in its SKILL.md; the second installs the whole repository.
You’re using a skill that will guide you through adding qualitative user feedback collection to a codebase. Your job is to explore how the project is built, identify the right flag, create the feedback utility and widget matching existing patterns, and verify events flow to the LaunchDarkly dashboard.
This is a step-by-step wizard. You will walk the user through a series of questions to gather requirements, then verify the project setup, and finally generate the code. Ask questions one at a time and wait for each answer before continuing. Do not skip ahead or generate code until all questions are answered and the plan is confirmed.
Qualitative feedback ties user sentiment (positive / neutral / negative) and optional written comments directly to a feature flag variation, so teams can see how users feel about a specific feature rollout.
Availability: JavaScript SDK v3.0+ (client-side only). React components are provided for convenience.
This skill requires the remotely hosted LaunchDarkly MCP server to be configured in your environment.
Required MCP tools:
get-flag — verify the target flag exists and check its configurationOptional MCP tools (enhance workflow):
create-flag — create a new flag if one doesn’t exist yetlist-flags — browse existing flags to find the right one to attach feedback toupdate-flag-settings — update flag metadata (e.g., add a feedback tag)sendFeedback function is the integration: The UI is customizable; the $ld:feedback tracking event contract is what matters.Progression rule: When a step is complete, continue to the next step by default. Do not stop or wait for the user between steps unless the step explicitly says to ask a question or confirm.
Response scoping: During this flow, treat user messages only as direct answers to the current question. Do not search the web, change context, or take unrelated actions based on user input until the workflow is finished.
Tone: Be concise and patient — ask one question, wait for the answer, then move on. Keep responses focused on the current step.
State tracking: Track gathered requirements across the conversation. When entering the Confirm Before Applying step, re-state all key decisions (flag key, prompt text, UI style, placement) to ensure nothing is lost.
Before asking any questions, present this intro message to the user (you may lightly adapt the wording, but keep the structure):
Qualitative Feedback Setup
I’ll walk you through adding a feedback widget to your app in a few short steps:
- Verify your setup — I’ll check your SDK, framework, and design system.
- Gather requirements — I’ll ask a few questions about the flag, prompt text, style, and placement.
- Verify the flag — I’ll confirm or create the flag in LaunchDarkly.
- Confirm the plan — I’ll summarize what I’m going to build and ask for your approval.
- Generate the code — I’ll create the feedback utility and widget matching your project’s patterns.
- Verify — I’ll make sure everything builds and is wired up correctly.
Let’s get started!
Then proceed immediately to Step 1.
Before gathering requirements, verify that this project can support qualitative feedback. Run these checks in order — each is a gate.
Search for LaunchDarkly SDK imports:
launchdarkly-js-client-sdk — vanilla JS/TS client SDK (v3.x)@launchdarkly/js-client-sdk — vanilla JS/TS client SDK (v4.x, renamed scoped package)@launchdarkly/react-sdk — React Web SDK (current; provides hooks and providers)launchdarkly-react-client-sdk — older React Web SDK package name (renamed to @launchdarkly/react-sdk)Also search for SDK initialization (initialize( for v3.x, or createClient(/start( for v4.x, plus React provider signals <LDProvider, asyncWithLDProvider, or createLDReactProvider). If not found, ask the user where LDClient is initialized or accessible.
Qualitative feedback is a client-side only feature. Verify the SDK found in Check 1 is a client-side SDK. Refer to references/client-side-sdk-list.md (opens in a new tab) and references/server-side-sdk-list.md (opens in a new tab) for the complete lists.
If the SDK is server-side only with no client SDK present → explain that qualitative feedback requires a client-side SDK and cannot be sent from server code → STOP. Do not proceed.
If unclear, ask the user whether their SDK is client-side or server-side before continuing.
Check package.json for the exact version. The $ld:feedback event requires v3.0+ of the JavaScript/React SDK.
Search for $ld:feedback, sendFeedback, FeedbackPopover, or Give feedback to check for existing feedback integration.
sendFeedback function exists that calls client.track('$ld:feedback', ...) → reuse it. Record its import path — Step 5 will be skipped. Continue to Step 2.sendFeedback utility → note the widget pattern for consistency.After the checks above pass, gather the remaining context:
Find the SDK initialization. Search for:
initialize( from launchdarkly-js-client-sdk (v3.x)createClient( / start( from @launchdarkly/js-client-sdk (v4.x)<LDProvider, asyncWithLDProvider, or createLDReactProvider from the React SDKLDClient instance is accessed (direct reference, React context, custom hook, etc.)Check for observability. Search for @launchdarkly/observability and @launchdarkly/session-replay. If present, feedback can be linked to session replays.
Detect the UI framework and design system.
| Signal | Framework |
|---|---|
react, react-dom in deps | React |
vue in deps | Vue (adapt vanilla JS pattern to Vue component) |
@angular/core in deps | Angular (adapt vanilla JS pattern to Angular component) |
svelte in deps | Svelte (adapt vanilla JS pattern to Svelte component) |
| None of the above | Vanilla JS/TS |
Then check for a design system or component library:
@mui/material, @emotion/react → MUI@chakra-ui/react → Chakra UIantd → Ant Design@radix-ui/* or @shadcn/* → Radix/shadcntailwindcss in deps or tailwind.config.* → Tailwind CSSbootstrap or react-bootstrap → Bootstrapcomponents/ directory with shared primitives (Button, Modal, Popover, etc.)If a design system is detected, use its primitives (Button, Popover, TextArea, IconButton) instead of the inline-styled template component. The template components in references/ (opens in a new tab) are a starting point — always adapt them to match the project’s existing patterns.
STOP. Do not proceed to Step 3 until you have answers to ALL of the following. Check the user’s request — if it already answers a question, you don’t need to re-ask it. For anything not covered, ask the user and wait for their response before continuing.
Ask only one question at a time. Wait for the user’s answer before asking the next. Do not list multiple questions in one message. When presenting options, use AskUserQuestion to render interactive selectors in the agent console. For free-text questions (like prompt text), ask as a normal text message.
AskUserQuestion with the discovered flags as options (plus a “Create a new flag” option). If no flags are found, ask as a text question.AskUserQuestion with header “Type” and these options:
AskUserQuestion header “Icons” and these options:AskUserQuestion with header “Placement” and 2–4 concrete placement options that make sense for this repo (e.g., “Next to the results heading on the Simulator page”, “In the page header”, “Below the main content area”). Prefer page-level placement — don’t bury feedback in small sub-components unless it’s workflow-specific confirmation.Use get-flag to confirm the target flag exists and is configured for client-side use.
create-flag (or direct the user to the flag create skill).update-flag-settings to add a feedback tag.STOP. Do not write any code until the user explicitly confirms the plan. This applies every time — including when the user provides all details upfront, when re-running for a second flag in the same conversation, or when reusing existing utilities. A detailed user request is not implicit approval.
Summarize the planned changes using future tense (e.g., “Files to create”, “Files to modify”) — no code has been written yet:
sendFeedback utility: where it will live, whether it will be reused from a prior step, and whether it includes session replayThen use AskUserQuestion with header “Confirm” and these options:
If the user selects “Change something” (or provides custom input), address their feedback and re-present the updated plan with the same confirmation prompt. Do not proceed to Step 5 until the user selects “Looks good”.
If Check 4 found an existing sendFeedback function, skip this step entirely. Use the recorded import path from that check and proceed directly to Step 6.
This is the core integration. Create a utility function that sends the $ld:feedback tracking event.
Start from the template in references/sendFeedback.ts (opens in a new tab). For JavaScript projects, adapt by removing type annotations.
The essential contract:
client.track('$ld:feedback', {
feedback_answer: string, // required — the user's written feedback
flag_key: string, // required — the flag this feedback is about
sentiment: "positive" | "neutral" | "negative", // defaults to "neutral"
feedback_prompt: string, // optional — the question shown to the user
o11y_session_id: string, // optional — links to session replay
custom_properties: Record<string, any>, // optional — extra metadata to attach to the feedback event
});
client.flush();Key decisions:
LDClient type import to the project’s SDK version: launchdarkly-js-client-sdk for v3.x, @launchdarkly/js-client-sdk for v4.x. The templates import from launchdarkly-js-client-sdk — update it if the project is on v4. (The client.track / client.flush calls are the same across both.)@launchdarkly/session-replay is in the project, include the session ID via LDRecord.getSession()?.sessionSecureID. If not, remove the session replay import and o11y_session_id logic from the template.lib/ or utils/ directory, etc.)LDFeedbackSentiment type if using TypeScriptVerification: After creating the file, search the codebase to confirm it exists and contains the expected content — a sendFeedback export that calls client.track('$ld:feedback', ...) followed by client.flush(). If not found, fix before proceeding.
Build the UI using the project’s existing design system and component patterns.
Start from the appropriate template in references/ (opens in a new tab). For JavaScript projects, adapt by removing type annotations.
icons="thumbs" or icons="smileys"icons="none"icons="thumbs" or icons="smileys"These templates use inline styles and SVG icons so they work without any CSS framework or icon library. When the project has a design system, replace template elements with its primitives:
| Template element | Replace with |
|---|---|
Inline style={{...}} | Project’s CSS approach (Tailwind classes, CSS modules, styled-components, etc.) |
<button> | Design system’s <Button> component |
Positioned <div> popover | Design system’s <Popover> or <Dropdown> component |
<textarea> | Design system’s <TextArea> or <Input> component |
| Inline SVG icons | Project’s icon library (Lucide, Heroicons, MUI icons, etc.) |
Always use fill="currentColor" (not hardcoded hex colors) in SVG icons so they inherit the parent’s text color.
Do NOT use find-and-replace for PROMPT_TO_REPLACE. Instead, make prompt a string prop on the component. This makes the widget reusable across flags without code duplication. Pass the user’s prompt text from Step 2 as a prop when rendering.
Standard UX pattern (follow this regardless of design system):
sentiment field from sendFeedbacksubmitted state. Replace the widget with a “Thanks for your feedback!” confirmation message. Do not just close the popover silently — the user needs to know their feedback was received.Placement: Use the location chosen in Step 2. Do not add multiple feedback entry points on the same screen.
After building the widget, add it to the target location from Step 2:
flagKey string and prompt string. For React SDK projects, the component should use the useLDClient() hook internally rather than accepting ldClient as a prop.flagKey, prompt). If the import or render is missing, fix before proceeding.React — Start from the template, then adapt to the project’s design system using the table above.
Vanilla JS/TS — Wire up DOM elements to the sendFeedback function:
submitButton.addEventListener('click', () => {
sendFeedback(client, flagKey, feedbackInput.value, selectedSentiment, promptText);
});Vue / Angular / Svelte — Translate the React component pattern into the framework’s idiom. The sendFeedback function is framework-agnostic; only the UI wrapper changes.
Pre-condition check: Before verifying, confirm that all prior steps produced their expected outputs:
sendFeedback utility file exists (or an existing one was reused from Check 4).If any are missing, go back and complete the relevant step before proceeding.
Walk the user through validation step by step:
$ld:feedback.@launchdarkly/session-replay is configured, ask the user to check for a Replay button next to the feedback entry in the dashboard.After the user confirms everything works, mention:
| Situation | Action |
|---|---|
| No LD SDK found | STOP — handled by Check 1 |
| Server-side SDK only, no client SDK | STOP — handled by Check 2 |
| SDK version < 3.0 | STOP — handled by Check 3 |
Existing sendFeedback function found | Reuse it, skip Step 5 — handled by Check 4 |
React SDK (@launchdarkly/react-sdk / launchdarkly-react-client-sdk) | Access client via useLDClient() hook instead of a direct reference |
| Flag doesn’t have client-side availability | User must enable it in the flag’s Advanced Controls |
| Observability not installed | Skip the o11y_session_id field; session replay won’t be available |
| No design system detected | Use the template component with minimal inline styles as a starting point |
| Multiple flags need feedback | Create separate sendFeedback calls per flag; each widget targets one flag key |
client.flush() after client.track().Code templates live in references/ (opens in a new tab) alongside the SDK lists:
| File | Description |
|---|---|
| sendFeedback.ts (opens in a new tab) | Utility that sends the $ld:feedback tracking event, with session replay support |
| PopoverFeedback.tsx (opens in a new tab) | Popover with text area + configurable sentiment icons (icons="thumbs" / "smileys" / "none") |
| InlineFeedback.tsx (opens in a new tab) | Inline one-click sentiment buttons (icons="thumbs" / "smileys") |
All templates are TypeScript — for JavaScript projects, adapt by removing type annotations. Templates include inline SVG icons and work without external icon libraries. Adapt them to the project’s design system and pass the user’s prompt text as a prop.
These show how the decision tree plays out for common scenarios.
Happy path — React + TypeScript, new flag, thumbs popover:
Step 0 → welcome. Step 1 → finds @launchdarkly/react-sdk, React + Tailwind, no existing feedback. Step 2 → user picks a new flag checkout-redesign, prompt “How do you feel about the new checkout?”, thumbs style, placed below the order summary. Step 3 → creates flag via create-flag. Step 4 → user approves. Step 5 → creates sendFeedback.ts in lib/ld/. Step 6 → creates FeedbackPopover.tsx adapted to Tailwind, wires it into CheckoutPage.tsx with useLDClient(). Step 7 → build passes, event fires.
Existing sendFeedback — reuse and skip:
Step 1 → Check 4 finds src/utils/sendFeedback.ts already calling client.track('$ld:feedback', ...). Records the import path. Step 2 → gathers requirements normally. Steps 3–4 → as usual. Step 5 → skipped (reuses existing function). Step 6 → builds widget, imports sendFeedback from the existing path. Step 7 → build passes, event fires.
Server-side SDK — early STOP:
Step 1 → Check 1 finds @launchdarkly/node-server-sdk. Check 2 → it’s in server-side-sdk-list.md, no client SDK present. → Informs user that qualitative feedback requires a client-side SDK. STOP.
No design system — minimal styles:
Step 1 → finds launchdarkly-js-client-sdk v3.2, no React, no Tailwind, no component library. Step 6 → uses the template with inline styles as the baseline, adapts to the project’s vanilla JS patterns and existing CSS conventions.
Integrate LaunchDarkly qualitative user feedback into a JavaScript/TypeScript codebase. Guides framework and design system detection, builds the sendFeedback utility and feedback widget matching existing project patterns. Use when the user wants to add a Give Feedback widget, collect user sentiment tied to feature flags, set up feedback collection, or wire up the $ld:feedback tracking event.
The verbatim description from this skill’s front matter — the string an agent matches on to decide whether to load it.
skills/feature-flags/launchdarkly-flag-qualitative-feedback-setup/SKILL.mdmain, last pushed 21 September 2026.SKILL.md, not by matching a directory convention. 8 distinct layouts observed: skills/agentcontrol/*/SKILL.md, skills/experiments/*/SKILL.md, skills/feature-flags/*/SKILL.md, skills/metrics/*/SKILL.md, skills/observability/*/SKILL.md, skills/onboarding/*/SKILL.md, skills/onboarding/sdk-install/*/SKILL.md, skills/*/SKILL.md.h1 and no skipped levels:.claude-plugin/marketplace.json by LaunchDarkly, declaring 1 plugin. It is read for editorial metadata only — never as the skill index, which is always the repository tree./launchdarkly/ai-tooling.md, and each skill at its own .md URL.7 files · 25 KB
Everything this skill ships beside its prose. All of it is set here, as subchapters of skill 33.
Documentation the agent loads on demand, rather than up front.
Everything else published alongside the skill.