Skip to main content

Add "In Honor of" / "In Memory of" Tribute Fields to Your Donation Page

This guide walks through adding tribute capture fields to a NationBuilder Donation v2 page. When enabled, donors can designate their gift as being made in honor or in memory of someone, and provide that person's name — all without leaving the donation for

📌 Note: This upgrade kit requires Custom Fields, which are only available on our Pro, Enterprise or Network plans. For help adjusting your plan please contact support at [email protected].

Supported themes: Raise, Momentum, Action, Aware

How it works

The form uses progressive disclosure. Nothing is pre-selected, ensuring donors who ignore the feature are not served a half-filled default.

  1. Page Load: A single checkbox appears: "This gift is in honor or memory of someone."

  2. Box Checked: The panel expands revealing the "In Honor of" and "In Memory of" radio options.

  3. Type Chosen: The text field expands to capture the "Full name of the person being honored or remembered."

Unchecking the box collapses the panel, clears the radio selection, and empties the name field. Because an unchecked radio group submits no key at all, a donation made without opening the section carries no tribute data.

Both values are written directly to the donation record, visible at Finances > Donations > Listing > [Edit] > Custom fields. They are filterable, included in CSV exports, and readable via API.

Before you begin

You will need:

  • Admin access to your NationBuilder control panel

  • Ability and Permissions to add Custom Fields

  • Access to your theme files via Theme > Current Custom Theme > Edit code

  • Basic familiarity with editing NationBuilder theme templates

Part 1 — Create the custom fields

You need to create two donation custom fields in your NationBuilder admin before adding any code to your theme. These fields are where the tribute data will be stored on each donation record.

Go to Settings > Defaults > Custom fields > Donations. Create the following two fields exactly as written below.

📌 Important: The slug is entered at creation time, cannot contain spaces, and cannot be changed afterward. The code references both slugs directly, so exact spelling is required.

Setting

Value

Label

Tribute type

Slug

tribute_type

Type

Multiple choice

Option 1

In Honor of

Option 2

In Memory of

Note on options: Use human-readable text for the options. NationBuilder uses the exact option text as both the stored value and the display text. Using human-readable strings ensures your admin records, CSV exports, and Finance filters are easily readable without translation.

Setting

Value

Label

Tribute name

Slug

tribute_name

Type

Text

Verify your fields: Load your live donation page and view the page source. Because custom fields auto-render, they will appear at the bottom of your form. Confirm the <select> for tribute type contains <option> strings that exactly match what you typed, including capitalization and spacing.

Part 2 — Add the shared CSS

This CSS applies to all four themes. Paste this block at the very bottom of theme.scss (Website > Theme > Current custom theme > Files > theme.scss).

/* ============================================
Tribute fields — shared base (all themes)
============================================ */

.tribute-block {
margin: 20px 0 10px;
}

/* The `hidden` attribute is the single source of truth for visibility.
!important guards against theme rules that set display on these containers. */
.tribute-block [hidden] {
display: none !important;
}

.tribute-toggle-row {
display: flex;
align-items: flex-start;
gap: 9px;
}

.tribute-toggle-row input[type="checkbox"] {
margin: 0;
flex: 0 0 auto;
position: relative;
top: 4px;
}

.tribute-toggle-row label {
margin: 0;
cursor: pointer;
font-weight: 600;
}

.tribute-panel {
margin-top: 14px;
padding: 16px 18px;
border: 1px solid rgba(0, 0, 0, .12);
border-radius: 4px;
background: rgba(0, 0, 0, .02);
}

/* Reset fieldset inheritance */
.tribute-type {
border: 0;
margin: 0 0 14px;
padding: 0;
min-width: 0;
}

.tribute-type-row {
display: flex;
flex-wrap: wrap;
gap: 10px 26px;
}

.tribute-radio {
display: flex;
align-items: center;
gap: 7px;
}

.tribute-radio input[type="radio"] {
margin: 0;
flex: 0 0 auto;
}

.tribute-radio label {
margin: 0;
cursor: pointer;
font-weight: normal;
}

.tribute-name-field {
margin-bottom: 0;
}

.tribute-name-field label {
display: block;
margin-bottom: 4px;
}

.tribute-name-field input[type="text"] {
max-width: 420px;
}

/* Visually hidden, still read aloud (Scoped to support Aware) */
.tribute-sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}

4. Save the file.

Part 3 — Add the JavaScript

Paste this script at the very bottom of pages_show_donation_v2_wide.html, immediately after the {% endform_for %} tag (next to the existing theme_donation_v2.js tag).

Do not wrap this in $(document).ready(): The script must sit after the markup to run at parse time, ensuring the cleanup handler binds before NationBuilder's deferred payment module fires its own submit listener.

<script>
/* ============================================
Tribute fields — 3-level progressive disclosure
Runs at parse time. No document.ready.
============================================ */
(function ($) {
'use strict';
if (!$) { return; }

var TYPE_NAME = 'donation[custom_values][tribute_type]';
var NAME_ID = 'donation_custom_values_tribute_name_custom';

var $toggle = $('#tribute-toggle');
if (!$toggle.length) { return; }

var $panel = $('#tribute-panel');
var $radios = $('input[name="' + TYPE_NAME + '"]');
var $nameField = $('#tribute-name-field');
var $name = $('#' + NAME_ID);

var motion = window.matchMedia('(prefers-reduced-motion: reduce)');
var booting = true;
function ms() { return (booting || motion.matches) ? 0 : 200; }

function reveal($el) {
if ($el.data('tributeOpen')) { return; }
$el.data('tributeOpen', true)
.stop(true, true)
.prop('hidden', false)
.css('display', 'none')
.slideDown(ms());
}

function conceal($el) {
if (!$el.data('tributeOpen') && $el.prop('hidden')) { return; }
$el.data('tributeOpen', false);
if ($el[0].contains(document.activeElement)) { $toggle.trigger('focus'); }
$el.stop(true, true).slideUp(ms(), function () {
if (!$el.data('tributeOpen')) {
$el.prop('hidden', true).css('display', '');
}
});
}

function enforce() {
var on = $toggle.prop('checked');
if (!on) { $radios.prop('checked', false); }
if (!on || !$radios.filter(':checked').length) { $name.val(''); }
$name.prop('required', false);
}

function render() {
var on = $toggle.prop('checked');
var chosen = $radios.filter(':checked').length > 0;

$toggle.attr('aria-expanded', String(on));

if (on) { reveal($panel); } else { conceal($panel); }
if (on && chosen) { reveal($nameField); } else { conceal($nameField); }
}

function sync() { enforce(); render(); }

$toggle.on('change', sync);
$radios.on('change', sync);

sync();
booting = false;

var form = document.querySelector('form[data-form="donation"]');
if (form) {
form.addEventListener('submit', enforce);
}
}(window.jQuery));
</script>

Part 4 — Add the HTML/Liquid tribute block

Step 4: Theme-Specific Code

4A: Suppress the Automatic Loop (All Themes)

Because NationBuilder auto-renders custom fields, you must suppress the default output so the fields don't render twice.

In pages_show_donation_v2_wide.html, locate the {% for cf in custom_fields.donation %} loop and add the bolded {% continue %} line immediately after the assign tag:

{% for cf in custom_fields.donation %}
{% assign custom_field = cf[1] %}
{% if custom_field.slug == 'tribute_type' or custom_field.slug == 'tribute_name' %}{% continue %}{% endif %}
{% assign custom_field_id = 'custom_values.' | append: custom_field.slug %}
{% comment %} …the rest of the stock loop, unchanged… {% endcomment %}
{% endfor %}

4B: Insert the Tribute Block

Place the HTML block based on your theme directly into pages_show_donation_v2_wide.html.

Placement rules:

  • Put it: After the employer/occupation block (is_employer_and_occupation_required?) and before the opt-in checkboxes (email_opt_in).

  • Never inside #address-info: NB hides this region on Apple Pay/Google Pay paths.

  • Never inside #cc-info or .payment-input: Rebuilt dynamically by NB's UI modifier.

  • Never inside .errorExplanation: Handled dynamically on form errors.

Choose your theme below for the specific Liquid and CSS additions.

Raise

Open raise/pages_show_donation_v2_wide.html and paste the following block at the insertion point described above.

HTML/Liquid:

{% comment %} ===== TRIBUTE FIELDS ===== {% endcomment %}
{% if custom_fields.donation.tribute_type %}
<div class="form-row">
<div class="form-group col-md-12">
<div class="tribute-block">
<div class="tribute-toggle-row">
<input type="checkbox" id="tribute-toggle" aria-expanded="false" aria-controls="tribute-panel">
<label for="tribute-toggle">This gift is in honor or memory of someone</label>
</div>
<div id="tribute-panel" class="tribute-panel" hidden>
<fieldset class="tribute-type">
<legend class="tribute-sr-only">Is this gift in honor of or in memory of someone?</legend>
<div class="tribute-type-row">
{% for opt in custom_fields.donation.tribute_type.multiple_choice_options %}
<div class="tribute-radio">
<input type="radio" id="tribute-type-{{ forloop.index }}" name="donation[custom_values][tribute_type]" value="{{ opt }}" aria-controls="tribute-name-field">
<label for="tribute-type-{{ forloop.index }}">{{ opt }}</label>
</div>
{% endfor %}
</div>
</fieldset>
<div id="tribute-name-field" class="tribute-name-field form-group" hidden>
<label for="donation_custom_values_tribute_name_custom">Full name of the person being honored or remembered</label>
{% text_field "custom_values.tribute_name", class:"text form-control", autocomplete:"off" %}
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% comment %} ===== END TRIBUTE FIELDS ===== {% endcomment %}

CSS (Append to theme.scss):

.tribute-block {

.tribute-block { 
.tribute-panel {
border-color: rgba(0, 0, 0, .10);
background: rgba(0, 0, 0, .015);
}
.tribute-name-field label {
position: static;
transform: none;
font-size: .875rem;
opacity: 1;
}
}

Momentum

Note: Momentum supports multilingual translations. For full translation support, create variables in your _translation_en.html (and other languages) for the static text, as Momentum's theme architecture requires.

HTML/Liquid:

{% comment %} ===== TRIBUTE FIELDS ===== {% endcomment %}
{% if custom_fields.donation.tribute_type %}
<div class="form-row">
<div class="form-group col-md-12">
<div class="tribute-block">
<div class="tribute-toggle-row">
<input type="checkbox" id="tribute-toggle" aria-expanded="false" aria-controls="tribute-panel">
<label for="tribute-toggle">This gift is in honor or memory of someone</label>
</div>
<div id="tribute-panel" class="tribute-panel" hidden>
<fieldset class="tribute-type">
<legend class="tribute-sr-only">Is this gift in honor of or in memory of someone?</legend>
<div class="tribute-type-row">
{% for opt in custom_fields.donation.tribute_type.multiple_choice_options %}
<div class="tribute-radio custom-control custom-radio">
<input type="radio" class="custom-control-input" id="tribute-type-{{ forloop.index }}" name="donation[custom_values][tribute_type]" value="{{ opt }}" aria-controls="tribute-name-field">
<label class="custom-control-label" for="tribute-type-{{ forloop.index }}">{{ opt }}</label>
</div>
{% endfor %}
</div>
</fieldset>
<div id="tribute-name-field" class="tribute-name-field form-group" hidden>
<label for="donation_custom_values_tribute_name_custom">Full name of the person being honored or remembered</label>
{% text_field "custom_values.tribute_name", class:"form-control", autocomplete:"off" %}
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% comment %} ===== END TRIBUTE FIELDS ===== {% endcomment %}

CSS (Append to theme.scss):

.tribute-block {
.tribute-panel {
border-color: #dde1e5;
background: #f8f9fa;
}
.tribute-radio.custom-radio {
gap: 0;
}
.tribute-name-field label {
font-size: .875rem;
margin-bottom: 6px;
}
}


Action

Open action/pages_show_donation_v2_wide.html. The Action theme uses a single-column form layout — the tribute block spans the full form width.

HTML/Liquid:

{% comment %} ===== TRIBUTE FIELDS ===== {% endcomment %}
{% if custom_fields.donation.tribute_type %}
<div class="row">
<div class="col-sm-12">
<div class="tribute-block">
<div class="tribute-toggle-row">
<input type="checkbox" id="tribute-toggle" aria-expanded="false" aria-controls="tribute-panel">
<label for="tribute-toggle">This gift is in honor or memory of someone</label>
</div>
<div id="tribute-panel" class="tribute-panel" hidden>
<fieldset class="tribute-type">
<legend class="tribute-sr-only">Is this gift in honor of or in memory of someone?</legend>
<div class="tribute-type-row">
{% for opt in custom_fields.donation.tribute_type.multiple_choice_options %}
<div class="tribute-radio">
<input type="radio" id="tribute-type-{{ forloop.index }}" name="donation[custom_values][tribute_type]" value="{{ opt }}" aria-controls="tribute-name-field">
<label for="tribute-type-{{ forloop.index }}">{{ opt }}</label>
</div>
{% endfor %}
</div>
</fieldset>
<div id="tribute-name-field" class="tribute-name-field form-group" hidden>
<label for="donation_custom_values_tribute_name_custom">Full name of the person being honored or remembered</label>
{% text_field "custom_values.tribute_name", class:"text form-control", autocomplete:"off" %}
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% comment %} ===== END TRIBUTE FIELDS ===== {% endcomment %}

CSS (Append to theme.scss):

.tribute-block {
.tribute-panel {
border-radius: 2px;
border-color: #ccc;
}
.tribute-name-field input[type="text"] {
max-width: 100%;
}
.tribute-name-field label {
display: block;
font-size: 14px;
font-weight: 600;
margin: 0 0 6px;
}
}

Aware

HTML/Liquid:

{% comment %} ===== TRIBUTE FIELDS ===== {% endcomment %}
{% if custom_fields.donation.tribute_type %}
<div class="row-fluid">
<div class="span12">
<div class="tribute-block">
<div class="tribute-toggle-row">
<input type="checkbox" id="tribute-toggle" aria-expanded="false" aria-controls="tribute-panel">
<label for="tribute-toggle">This gift is in honor or memory of someone</label>
</div>
<div id="tribute-panel" class="tribute-panel" hidden>
<fieldset class="tribute-type">
<legend class="tribute-sr-only">Is this gift in honor of or in memory of someone?</legend>
<div class="tribute-type-row">
{% for opt in custom_fields.donation.tribute_type.multiple_choice_options %}
<div class="tribute-radio">
<input type="radio" id="tribute-type-{{ forloop.index }}" name="donation[custom_values][tribute_type]" value="{{ opt }}" aria-controls="tribute-name-field">
<label for="tribute-type-{{ forloop.index }}">{{ opt }}</label>
</div>
{% endfor %}
</div>
</fieldset>
<div id="tribute-name-field" class="tribute-name-field" hidden>
<label for="donation_custom_values_tribute_name_custom">Full name of the person being honored or remembered</label>
{% text_field "custom_values.tribute_name", class:"text", autocomplete:"off" %}
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% comment %} ===== END TRIBUTE FIELDS ===== {% endcomment %}

CSS (Append to theme.scss):

.tribute-block {
.tribute-panel {
border-color: #b0b8bf;
border-width: 2px;
}
.tribute-name-field input[type="text"] {
width: 100%;
max-width: 420px;
box-sizing: border-box;
}
.tribute-name-field label {
font-weight: 600;
}
}

Did this answer your question?