Qaid
ARTICLE

Custom Modal Content: Tailor the Feedback Form

Customize the feedback modal's title, placeholder text, submit button, and add custom fields.

Qaid Team

The thumbs tell you the direction. The modal is where you find out why, and what you write in it decides whether anybody bothers.

What is in the modal

A title acknowledging the vote, a subtitle asking for more, a textarea with placeholder text, a submit button, and a skip button that sends the vote without a message. Click through it:

Default Modal

Click a button to see the standard modal

Loading embed...

Eight strings in the text object

Every word in that modal is a default you can replace:

PropertyDefaultDescription
modalTitle"Thank you for your feedback!"The main heading in the modal
modalSubtitle"Would you like to add a message..."Explanatory text below the title
placeholder"Optional: Tell us more..."Placeholder text in the textarea
submitButton"Submit"Text on the primary action button
skipButton"Skip"Text on the secondary action button
confirmationTitle"Thank you!"Heading on the success screen
confirmationMessage"Your feedback has been received."Body copy on the success screen
confirmationClose"Close"Text on the success screen's dismiss button

For a product team

Product Feedback

Customized for product teams

Loading embed...
new QaidFeedback({
  endpoint: '/api/feedback',
  text: {
    modalTitle: "We appreciate your input!",
    modalSubtitle: "Help our product team understand what you experienced.",
    placeholder: "Describe what happened, what you expected, or what could be improved...",
    submitButton: "Send Feedback",
    skipButton: "Not now"
  }
});

For bug reports

Bug Report Style

Optimized for collecting bug reports

Loading embed...
new QaidFeedback({
  endpoint: '/api/feedback',
  text: {
    modalTitle: "Thanks for reporting this issue",
    modalSubtitle: "The more detail you provide, the faster we can fix it.",
    placeholder: "Steps to reproduce: 1) ... 2) ... 3) ...",
    submitButton: "Report Bug",
    skipButton: "Skip details"
  }
});

Stripped back

Minimal Style

Short and to the point

Loading embed...
new QaidFeedback({
  endpoint: '/api/feedback',
  text: {
    modalTitle: "Got it!",
    modalSubtitle: "Anything else?",
    placeholder: "Type here...",
    submitButton: "Done",
    skipButton: "Nope"
  }
});

Where the modal appears

With targeting on, the modal anchors to whatever element the reader picked. With skipTargeting: true it anchors near the buttons instead.

The order it tries

Below the target first. Above it if there is no room below. Then clamped so no edge leaves the viewport, with an arrow pointing back at where the click landed.

It keeps 8px off the target and 16px off the viewport edges.

Making it wider

modalWidth takes a number of pixels:

new QaidFeedback({
  endpoint: '/api/feedback',
  modalWidth: 500  // Default is 400
});

Worth it if your placeholder runs to two lines. Not worth it otherwise, since a wide box on a phone just becomes a tall one.

Restyling it with CSS

The structure is fixed, the appearance is not. Pass a stylesheet through css and target these:

/* The card itself */
.qaid-modal-box {
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}

.qaid-modal-title {
  color: #1a1a1a;
  font-size: 1.125rem;
  font-weight: 600;
}

.qaid-modal-subtitle {
  color: #666;
  font-size: 0.875rem;
}

.qaid-textarea {
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  resize: vertical;
}

/* Both actions share this class; colour comes from colors.marker */
button.qaid-btn-submit {
  border-radius: 8px;
}

Set the backdrop through backdropOpacity rather than restyling .qaid-backdrop, and the button colour through colors.marker rather than CSS. Styling the modal has the full selector list, including the mobile bottom sheet, which uses different outer classes.

What config does without CSS

Three of the common changes are settings:

new QaidFeedback({
  endpoint: '/api/feedback',
  // Modal width in pixels
  modalWidth: 400,
  // Backdrop opacity (0-1)
  backdropOpacity: 0.3,
  // Font for all text
  fontFamily: 'Inter, system-ui, sans-serif',
  // Base font size
  fontSize: 16,
  // Colors affect modal buttons
  colors: {
    positive: '#22c55e',  // Submit button color for positive feedback
    negative: '#ef4444'   // Submit button color for negative feedback
  }
});

Custom Styled

Modified with configuration options

Loading embed...

The message is always optional

Skip sends the vote with no text. That is deliberate: a required message turns a one-click rating into a writing task, and most people close the tab instead.

You cannot make it mandatory, but you can lean either way with the copy.

Asking harder

Name what you want and the skip button gets pressed less:

new QaidFeedback({
  endpoint: '/api/feedback',
  text: {
    modalSubtitle: "Please tell us more so we can take action.",
    placeholder: "We need details to investigate this...",
    skipButton: "Submit without details"
  }
});

Getting out of the way

Or say plainly that one word is fine:

new QaidFeedback({
  endpoint: '/api/feedback',
  text: {
    modalSubtitle: "Optionally add context (or just skip ahead).",
    placeholder: "No pressure - only if you have time!",
    skipButton: "Skip"
  }
});

The success screen

A sent message gets a checkmark and a line of acknowledgement instead of the modal just vanishing. That matters most for embeds mounted in the page, where nothing overlays anything, so a silent close looks exactly like a failure.

On by default since v1.7.0. All three strings are yours:

new QaidFeedback({
  endpoint: '/api/feedback',
  text: {
    confirmationTitle: "Message received",
    confirmationMessage: "Thanks — we read every one of these.",
    confirmationClose: "Done"
  }
});

Turning it off

hideConfirmation: true restores the pre-1.7.0 behaviour and closes straight away:

new QaidFeedback({
  endpoint: '/api/feedback',
  hideConfirmation: true
});

Or as a data attribute:

<script
  src="https://unpkg.com/@qaiddev/thumbs-embed"
  data-endpoint="/api/feedback"
  data-hide-confirmation="true"
></script>

It only shows on a delivered message. A failed request closes the modal the old way, because thanking somebody for feedback you never received is worse than saying nothing.

When screenshots are on

The modal is where you tell people an image is attached:

new QaidFeedback({
  endpoint: '/api/feedback',
  captureScreenshot: true,
  text: {
    modalTitle: "Screenshot captured!",
    modalSubtitle: "Add notes to explain what you see in the screenshot.",
    placeholder: "Point out what's wrong or what you'd like to change..."
  }
});

Alongside a theme

Strings and styling set together:

Branded Experience

Text + visual customization

Loading embed...
new QaidFeedback({
  endpoint: '/api/feedback',
  colors: {
    positive: "#a78bfa",
    negative: "#f472b6"
  },
  text: {
    modalTitle: "You're awesome!",
    modalSubtitle: "Help us make this even better.",
    placeholder: "Your ideas matter to us...",
    submitButton: "Send love",
    skipButton: "Skip for now"
  }
});

As data attributes

Every string works on a script tag too:

<script
  src="https://unpkg.com/@qaiddev/thumbs-embed/dist/embed.js"
  data-endpoint="/api/feedback"
  data-modal-title="We value your feedback!"
  data-modal-subtitle="Tell us what you think."
  data-placeholder="Share your experience..."
  data-submit-button="Send"
  data-skip-button="Skip"
  data-modal-width="450"
  data-backdrop-opacity="0.4"
></script>

Writing the copy

Six words in the title, because it gets scanned rather than read. Ask for one specific thing in the subtitle, since "tell us more" gets you "it's broken" and "which page was this on?" gets you an answer. Start the buttons with verbs.

Then spend a minute on the skip button, which is the string nobody edits. "Skip", "Not now" and "No thanks" send quite different signals about whether you wanted the message in the first place.

Back to all articles