Yes! This is very possible with Smarty (formerly SmartyStreets) in Hubspot. You’ll create a custom HTML form that communicates to Hubspot through the form API.
It sounds complicated, and it kind of is, but not really.
Smarty released a tutorial on it last year: Implementing US Address Autocomplete in HubSpot
Basically, you create an HTML form, link it to the Hubspot form API, and then connect it to Smarty’s autocomplete functionality. Pretty simple.
Here’s basic code for an HTML form that is prepared for autocomplete:
<form id="myCoolForm" style="padding: 30px; border: 1px solid #476884; border-radius: 16px;"><input id="myCoolFirstname" style="padding: 15px; margin: 6px; border: 2px solid #eee; border-radius: 2px;" type="text" placeholder="First name *"> <input id="myCoolLastname" style="padding: 15px; margin: 6px; border: 2px solid #eee; border-radius: 2px;" type="text" placeholder="Last name *"> <input id="myCoolEmail" style="padding: 15px; margin: 6px; border: 2px solid #eee; border-radius: 2px;" required="" type="text" placeholder="Email *"> <input id="myCoolAddress" style="padding: 15px; margin: 6px; border: 2px solid #eee; border-radius: 2px;" type="text" placeholder="Address *"> <input id="myCoolCity" style="padding: 15px; margin: 6px; border: 2px solid #eee; border-radius: 2px;" type="text" placeholder="City *"><select id="myCoolState" style="padding: 15px; margin: 6px; border: 2px solid #eee; border-radius: 2px;">
<option value="AL">AL</option>
<option value="AK">AK</option>
<option value="AZ">AZ</option>
<option value="AR">AR</option>
<option value="CA">CA</option>
<option value="CO">CO</option>
<option value="CT">CT</option>
<option value="DE">DE</option>
<option value="FL">FL</option>
<option value="GA">GA</option>
<option value="HI">HI</option>
<option value="ID">ID</option>
<option value="IL">IL</option>
<option value="IN">IN</option>
<option value="IA">IA</option>
<option value="KS">KS</option>
<option value="KY">KY</option>
<option value="LA">LA</option>
<option value="ME">ME</option>
<option value="MD">MD</option>
<option value="MA">MA</option>
<option value="MI">MI</option>
<option value="MN">MN</option>
<option value="MS">MS</option>
<option value="MO">MO</option>
<option value="MT">MT</option>
<option value="NE">NE</option>
<option value="NV">NV</option>
<option value="NH">NH</option>
<option value="NJ">NJ</option>
<option value="NM">NM</option>
<option value="NY">NY</option>
<option value="NC">NC</option>
<option value="ND">ND</option>
<option value="OH">OH</option>
<option value="OK">OK</option>
<option value="OR">OR</option>
<option value="PA">PA</option>
<option value="RI">RI</option>
<option value="SC">SC</option>
<option value="SD">SD</option>
<option value="TN">TN</option>
<option value="TX">TX</option>
<option value="UT">UT</option>
<option value="VT">VT</option>
<option value="VA">VA</option>
<option value="WA">WA</option>
<option value="WV">WV</option>
<option value="WI">WI</option>
<option value="WY">WY</option>
</select><input id="myCoolZip" style="padding: 15px; margin: 6px; border: 2px solid #eee; border-radius: 2px;" type="text" placeholder="ZIP Code">
<div style="padding-bottom: 15px; padding-left: 15px;">Your <a href="https://YOUR PRIVACY POLICY LINK GOES HERE">privacy</a> is important to us. You agree to receive messages regarding our products and services by submitting this form. You may unsubscribe at any time.</div>
<div style="text-align: center;"><button id="myCoolButton" style="background-color: #0066ff; color: white; padding: 15px 40px; font-size: 20px; font-weight: bold; border: none; border-radius: 90px; cursor: pointer; display: inline-block; text-align: center; text-decoration: none;">SUBMIT</button></div>
</form>
And then whatever page you’re putting it on just needs to have this JavaScript in the footer.
<script>
// HubSpot Settings - change stuff between the quotes after the equals sign (keep the quotes).
const MY_COOL_HUBSPOT_FORM_ID = 'YOUR HUBSPOT FORM ID';
const MY_COOL_HUBSPOT_PORTAL_ID = 'YOUR HUBSPOT PORTAL ID';
const MY_COOL_CONFIRMATION_PAGE = 'YOUR CONFIRMATION PAGE URL BEGINNING WITH https://';
const MY_COOL_ERROR_MESSAGE = 'All fields are required.';
// Smarty Settings - change stuff between the quotes after the equals sign (keep the quotes).
const MY_COOL_SMARTY_EMBEDDED_KEY = 'YOUR SMARTY EMBEDDED KEY GOES HERE';
const MY_COOL_SMARTY_SOURCE = 'postal'; // valid options are 'postal' or 'all'
// ------ DON'T MODIFY BELOW THIS LINE ------
document.getElementById('myCoolButton').addEventListener('click', (e) => {
e.preventDefault();
submitData();
});
async function submitData() {
const url = `https://api.hsforms.com/submissions/v3/integration/submit/${MY_COOL_HUBSPOT_PORTAL_ID}/${MY_COOL_HUBSPOT_FORM_ID}`;
const headers = {
'accept': 'application/json',
'content-type': 'application/json',
};
const data = {
"fields": [{
"name": "firstname",
"value": document.getElementById('myCoolFirstname').value
},
{
"name": "lastname",
"value": document.getElementById('myCoolLastname').value
},
{
"name": "email",
"value": document.getElementById('myCoolEmail').value
},
{
"name": "address",
"value": document.getElementById('myCoolAddress').value
},
{
"name": "city",
"value": document.getElementById('myCoolCity').value
},
{
"name": "state",
"value": document.getElementById('myCoolState').value
},
{
"name": "zip",
"value": document.getElementById('myCoolZip').value
}
],
"skipValidation": false,
"context": {}
};
try {
const response = await fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const responseData = await response.json();
console.log(responseData);
window.location.href = MY_COOL_CONFIRMATION_PAGE;
} catch (error) {
console.error('Error:', error);
alert(MY_COOL_ERROR_MESSAGE);
}
}
((global) => {
let settings = {
embeddedKey: '',
addressId: 'address',
cityId: 'city',
stateId: 'state',
zipCodeId: 'zip',
styleBackgroundColorHexString: '#fff',
styleColorHexString: '#333',
styleHoverBackgroundColorHexString: '#cfe8ff',
styleHoverColorHexString: '#000',
styleBorderColorHexString: '#e0e0e0',
styleBorderPixelWidthInt: 2,
styleBorderRadiusInt: 8,
styleFontFamilyString: 'sans-serif',
styleFontSizePixelInt: 14,
styleRowPaddingString: '8px',
styleBoxPixelWidthInt: 300,
styleBoxPixelHeightInt: 300,
styleSelectedSuggestionColorHexString: '#000',
styleSelectedSuggestionBackgroundColorHexString: '#ddd',
suggestionElement: document.createElement('div'),
suggestionId: null,
offsetHeight: 20,
addressElement: null,
activeStyles: '',
inactiveStyles: 'display: none;',
selectedIndex: 0,
lastAction: '',
};
const wrapperStyles = `
display: inline-block;
position: relative;
width: 100%;
`;
const extendSettings = (defaults, options) => {
// @todo ensure the options are allowed.
return {
...defaults,
...options
};
};
const wrapElementsWithDiv = (elementId) => {
const knownElement = document.getElementById(elementId);
if (!knownElement) {
console.error(`Element with ID ${elementId} not found.`);
return;
}
settings.addressElement = knownElement;
settings.offsetHeight = knownElement.offsetHeight;
const wrapperDiv = document.createElement('div');
wrapperDiv.style.cssText = wrapperStyles;
knownElement.parentNode.insertBefore(wrapperDiv, knownElement);
wrapperDiv.appendChild(knownElement);
knownElement.parentNode.insertBefore(settings.suggestionElement, knownElement.nextSibling);
}
const sendLookupRequest = async (searchValue, selected = "") => {
const params = new URLSearchParams({
key: settings.embeddedKey,
search: searchValue,
source: MY_COOL_SMARTY_SOURCE,
selected
});
try {
const request = await fetch(
`https://us-autocomplete-pro.api.smarty.com/lookup?${params}`
);
const data = await request.json();
if (data?.suggestions?.length > 0) formatSuggestions(data.suggestions);
} catch (e) {
console.error(e.message);
}
};
const applyStyles = (element, styles) => {
for (let property in styles) {
element.style[property] = styles[property];
}
};
const formatSuggestions = (suggestions) => {
const {
suggestionElement,
inactiveStyles,
styleRowPaddingString,
styleHoverBackgroundColorHexString,
styleHoverColorHexString,
styleBackgroundColorHexString,
styleColorHexString,
styleFontSizePixelInt,
styleSelectedSuggestionColorHexString,
styleSelectedSuggestionBackgroundColorHexString,
activeStyles,
} = settings;
suggestionElement.innerHTML = '';
suggestionElement.style.cssText = activeStyles;
const formattedSuggestions = suggestions.map((suggestion, index) => {
const divElement = document.createElement("div");
divElement.classList.add('smarty-suggestion');
divElement.style['padding'] = styleRowPaddingString;
divElement.style['fontSize'] = `${styleFontSizePixelInt}px`;
if (index === 0) {
applyStyles(divElement, {
color: styleSelectedSuggestionColorHexString,
backgroundColor: styleSelectedSuggestionBackgroundColorHexString,
});
settings.selectedIndex = 0;
}
const {
street_line,
city,
state,
zipcode,
secondary,
entries
} = suggestion;
const hasSecondaryData = secondary && entries > 1;
divElement.innerText = `${street_line} ${secondary} ${
hasSecondaryData ? `(${entries} entries)` : ""
} ${city} ${state} ${zipcode}`;
divElement.addEventListener('mouseover', () => {
if (settings.lastAction === 'keyboard') return;
applyStyles(divElement, {
backgroundColor: styleHoverBackgroundColorHexString,
color: styleHoverColorHexString,
});
});
divElement.addEventListener('mouseout', () => {
applyStyles(divElement, {
backgroundColor: styleBackgroundColorHexString,
color: styleColorHexString,
});
});
divElement.addEventListener("click", async () => {
const streetLineWithSecondary = `${street_line} ${secondary}`.trim();
if (hasSecondaryData) {
const selected = `${streetLineWithSecondary} (${entries}) ${city} ${state} ${zipcode}`;
await sendLookupRequest(streetLineWithSecondary, selected);
} else {
suggestionElement.style.cssText = inactiveStyles;
}
populateForm({
streetLineWithSecondary,
city,
state,
zipcode
});
});
return divElement;
});
suggestionElement.append(...formattedSuggestions);
}
const populateForm = ({
streetLineWithSecondary,
city,
state,
zipcode
}) => {
const {
addressId,
cityId,
stateId,
zipCodeId
} = settings;
document.getElementById(addressId).value = streetLineWithSecondary;
document.getElementById(cityId).value = city;
document.getElementById(stateId).value = state;
document.getElementById(zipCodeId).value = zipcode;
};
const scrollWrapperToSelected = () => {
const {
selectedIndex,
suggestionElement
} = settings;
const elements = document.getElementsByClassName('smarty-suggestion');
if (selectedIndex >= 0 && selectedIndex < elements.length) {
const selectedChild = elements[selectedIndex];
const wrapperRect = suggestionElement.getBoundingClientRect();
const selectedRect = selectedChild.getBoundingClientRect();
// Check if selected child is above the viewport
if (selectedRect.top < wrapperRect.top) {
suggestionElement.scrollTop -= (wrapperRect.top - selectedRect.top);
}
// Check if selected child is below the viewport
else if (selectedRect.bottom > wrapperRect.bottom) {
suggestionElement.scrollTop += (selectedRect.bottom - wrapperRect.bottom);
}
}
}
LikeButtaSmartyUsAddressAutocomplete = (userSettings) => {
settings = extendSettings(settings, userSettings);
wrapElementsWithDiv(settings.addressId);
settings.activeStyles = `
display: block;
position: absolute;
overflow-y: auto;
cursor: pointer;
top: ${settings.offsetHeight};
width: ${settings.styleBoxPixelWidthInt}px;
height: ${settings.styleBoxPixelHeightInt}px;
border: solid ${settings.styleBorderPixelWidthInt}px ${settings.styleBorderColorHexString};
border-radius: ${settings.styleBorderRadiusInt}px;
background-color: ${settings.styleBackgroundColorHexString};
font-family: ${settings.styleFontFamilyString};
color: ${settings.styleColorHexString};
`;
settings.suggestionElement.id = 'smartySuggestionBox';
settings.suggestionId = settings.suggestionElement.id;
const {
addressElement,
suggestionElement,
inactiveStyles,
activeStyles
} = settings;
suggestionElement.style.cssText = inactiveStyles;
addressElement.addEventListener("keyup", (e) => {
if ([
'Shift', 'Control', 'Alt', 'Meta', 'CapsLock', 'Tab', 'ArrowUp', 'ArrowDown',
'ArrowLeft', 'ArrowRight', 'Escape', 'F1', 'F2', 'F3', 'F4', 'F5',
'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12'
].includes(e.key)) return;
if (e.key === 'Enter') {
const elements = document.getElementsByClassName('smarty-suggestion');
elements[settings.selectedIndex].click();
return;
}
const searchValue = e.target.value;
if (!searchValue) {
suggestionElement.style.cssText = inactiveStyles;
return;
}
sendLookupRequest(searchValue);
});
addressElement.addEventListener("keydown", async (e) => {
if (!['ArrowUp', 'ArrowDown'].includes(e.key)) return;
e.preventDefault();
const {
selectedIndex,
styleColorHexString,
styleBackgroundColorHexString,
styleSelectedSuggestionColorHexString,
styleSelectedSuggestionBackgroundColorHexString,
} = settings;
const elements = document.getElementsByClassName('smarty-suggestion');
[...elements].forEach((element) => {
applyStyles(element, {
color: styleColorHexString,
backgroundColor: styleBackgroundColorHexString,
});
});
if (elements.length === selectedIndex + 1 && e.key === 'ArrowDown') {
settings.selectedIndex = 0;
} else if (selectedIndex === 0 && e.key === 'ArrowUp') {
settings.selectedIndex = elements.length - 1;
} else if (e.key === 'ArrowDown') {
settings.selectedIndex += 1;
} else {
settings.selectedIndex -= 1;
}
applyStyles(elements[settings.selectedIndex], {
color: styleSelectedSuggestionColorHexString,
backgroundColor: styleSelectedSuggestionBackgroundColorHexString,
});
settings.lastAction = 'keyboard';
scrollWrapperToSelected();
});
suggestionElement.addEventListener('mousemove', (e) => {
settings.lastAction = 'mouse';
});
suggestionElement.addEventListener('mouseout', (e) => {
const elements = document.getElementsByClassName('smarty-suggestion');
const {
selectedIndex,
styleSelectedSuggestionColorHexString,
styleSelectedSuggestionBackgroundColorHexString
} = settings;
applyStyles(elements[selectedIndex], {
color: styleSelectedSuggestionColorHexString,
backgroundColor: styleSelectedSuggestionBackgroundColorHexString,
});
});
document.addEventListener('click', (e) => {
if (!suggestionElement.contains(e.target) && !e.target.classList.contains('smarty-suggestion')) {
suggestionElement.style.cssText = inactiveStyles;
}
});
};
global.LikeButtaSmartyUsAddressAutocomplete = LikeButtaSmartyUsAddressAutocomplete;
})(window);
LikeButtaSmartyUsAddressAutocomplete({
embeddedKey: MY_COOL_SMARTY_EMBEDDED_KEY,
addressId: 'myCoolAddress',
cityId: 'myCoolCity',
stateId: 'myCoolState',
zipCodeId: 'myCoolZip',
});
</script>
I got it working really fast, in like ten minutes.