How to add company logo field to Memberstack member profiles when only one image field exists by default? Answered
Does anyone know how we'd add in an additional photo field for profiles?
I'm setting up an instance where we have a Profile image as well as a Company logo image, they need to be something which users can update on their own. Unsure if there's already a custom field for that or what the recommended flow would be (I'm guessing it'll require a form with an upload field with form data set to public so that the field essentially pulls in the URL from the uploaded asset on Webflows servers?)
Julian Galluzzo any ideas on the best way to implement a second image for datasets? We’re wanting to display profile pic + company logos in profiles of members
Comments
5 comments
Hey Waldo! Not the easiest, but i think the best way would be using this https://www.memberstack.com/scripts/97-upload-files-to-s3-bucket
Thank you so much, Julian Galluzzo! Any idea if the team has plans to add at least 1-2 more profile fields to host images under a certain file size or something, Duncan from Memberstack? Seems like more of a copy/paste/integration update via Memberstack compared to the lift of sending customers over to S3.
Hey Waldo Broodryk 👋 No plans at this time, but I agree it would be awesome to be able to upload/manage files directly in MS.
Gooootcha, thank you for letting me know, Duncan from Memberstack. We'll probably just host the image assets for those in Webflow + map the image link value to a database field in Memberstack since there won't be that many to update each time.
Hey! Duncan Hamra I just built a complete solution for this. Here's how you can add both profile picture AND company logo with a nice overlapping display effect (where both images are visible but the profile pic overlaps the bottom-right corner of the logo). - Demo
The Setup:
Since Memberstack doesn't have built-in support for multiple image fields, I used the custom JSON fields approach. Here's what worked perfectly for me:
Step 1: Add Custom Fields in Memberstack
profilePictureUrl(type: Text)companyLogoUrl(type: Text)Step 2: The Solution
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Member Profile & Logo System</title><!-- Memberstack Script --><script src="https://api.memberstack.com/static/memberstack.js?custom" data-memberstack-id="YOUR_MEMBERSTACK_ID"></script><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);min-height: 100vh;padding: 40px 20px;}.container {max-width: 800px;margin: 0 auto;}.profile-card {background: white;border-radius: 20px;padding: 40px;box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);margin-bottom: 30px;}/* ============================================OVERLAPPING IMAGES DISPLAY============================================ */.images-container {position: relative;width: 280px;height: 150px;margin: 0 auto 40px;}.company-logo-display {position: absolute;left: 0;top: 0;width: 180px;height: 120px;object-fit: contain;background: #f8f9fa;border-radius: 16px;padding: 15px;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);border: 4px solid white;}.profile-picture-display {position: absolute;right: 0;bottom: 0;width: 120px;height: 120px;object-fit: cover;border-radius: 50%;border: 5px solid white;box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);z-index: 2;}/* ============================================EDIT FORM STYLES============================================ */h1 {text-align: center;color: #1a202c;margin-bottom: 10px;font-size: 28px;}.subtitle {text-align: center;color: #718096;margin-bottom: 40px;font-size: 16px;}.form-section {margin-bottom: 30px;padding: 25px;background: #f8f9fa;border-radius: 12px;}.form-section h2 {color: #2d3748;font-size: 20px;margin-bottom: 20px;display: flex;align-items: center;gap: 10px;}.icon {width: 24px;height: 24px;}label {display: block;font-weight: 600;color: #4a5568;margin-bottom: 8px;font-size: 14px;}input[type="text"] {width: 100%;padding: 14px 16px;border: 2px solid #e2e8f0;border-radius: 10px;font-size: 16px;transition: all 0.3s;margin-bottom: 12px;}input[type="text"]:focus {outline: none;border-color: #667eea;box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);}.helper-text {font-size: 13px;color: #718096;margin-bottom: 15px;line-height: 1.5;}.helper-text a {color: #667eea;text-decoration: none;font-weight: 600;}.helper-text a:hover {text-decoration: underline;}.preview-section {margin-top: 15px;padding: 15px;background: white;border-radius: 8px;display: none;}.preview-section.show {display: block;}.preview-label {font-size: 12px;color: #718096;margin-bottom: 8px;font-weight: 600;text-transform: uppercase;letter-spacing: 0.5px;}.preview-img {max-width: 100%;max-height: 150px;border-radius: 8px;border: 2px solid #e2e8f0;}button {width: 100%;padding: 16px;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);color: white;border: none;border-radius: 10px;font-size: 16px;font-weight: 600;cursor: pointer;transition: all 0.3s;display: flex;align-items: center;justify-content: center;gap: 10px;}button:hover {transform: translateY(-2px);box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4);}button:disabled {opacity: 0.6;cursor: not-allowed;transform: none;}.status-message {margin-top: 15px;padding: 14px 18px;border-radius: 10px;font-size: 14px;font-weight: 600;display: none;animation: slideIn 0.3s ease;}@keyframes slideIn {from {opacity: 0;transform: translateY(-10px);}to {opacity: 1;transform: translateY(0);}}.status-message.show {display: block;}.status-message.success {background: #d1fae5;color: #065f46;border: 2px solid #34d399;}.status-message.error {background: #fee2e2;color: #dc2626;border: 2px solid #fca5a5;}.spinner {width: 20px;height: 20px;border: 3px solid rgba(255, 255, 255, 0.3);border-top-color: white;border-radius: 50%;animation: spin 0.8s linear infinite;}@keyframes spin {to { transform: rotate(360deg); }}.image-sources {background: #e0e7ff;padding: 20px;border-radius: 10px;margin-top: 30px;}.image-sources h3 {color: #3730a3;font-size: 16px;margin-bottom: 12px;}.image-sources ul {list-style: none;padding-left: 0;}.image-sources li {color: #4c1d95;margin-bottom: 8px;padding-left: 20px;position: relative;font-size: 14px;}.image-sources li:before {content: "→";position: absolute;left: 0;font-weight: bold;}.loading-overlay {position: fixed;top: 0;left: 0;right: 0;bottom: 0;background: rgba(102, 126, 234, 0.95);display: flex;align-items: center;justify-content: center;flex-direction: column;color: white;font-size: 20px;z-index: 9999;}.loading-overlay .spinner {width: 50px;height: 50px;border-width: 5px;margin-bottom: 20px;}@media (max-width: 600px) {.profile-card {padding: 25px 20px;}.images-container {width: 240px;height: 130px;}.company-logo-display {width: 150px;height: 100px;}.profile-picture-display {width: 100px;height: 100px;}h1 {font-size: 24px;}}</style></head><body><div id="loadingOverlay" class="loading-overlay" style="display: none;"><div class="spinner"></div><div>Loading your profile...</div></div><div class="container"><!-- Display Card --><div class="profile-card"><h1>Member Profile</h1><p class="subtitle">Your profile picture and company logo</p><div class="images-container"><img id="companyLogoDisplay" class="company-logo-display"src="https://via.placeholder.com/180x120/e2e8f0/64748b?text=Company+Logo"alt="Company Logo"><img id="profilePictureDisplay" class="profile-picture-display"src="https://via.placeholder.com/120/cbd5e1/475569?text=Profile"alt="Profile Picture"></div></div><!-- Edit Form --><div class="profile-card"><h1>✏️ Edit Images</h1><p class="subtitle">Update your profile picture and company logo</p><!-- Profile Picture Section --><div class="form-section"><h2><svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg>Profile Picture</h2><label for="profilePictureUrl">Image URL</label><inputtype="text"id="profilePictureUrl"placeholder="https://example.com/your-photo.jpg"><p class="helper-text">Paste a direct link to your profile photo. Best size: 400x400px</p><div id="profilePreview" class="preview-section"><div class="preview-label">Preview</div><img id="profilePreviewImg" class="preview-img" src="" alt="Preview"></div><button id="updateProfileBtn"><svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path></svg>Update Profile Picture</button><div id="profileStatus" class="status-message"></div></div><!-- Company Logo Section --><div class="form-section"><h2><svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"></path></svg>Company Logo</h2><label for="companyLogoUrl">Image URL</label><inputtype="text"id="companyLogoUrl"placeholder="https://example.com/company-logo.png"><p class="helper-text">Paste a direct link to your company logo. Works best with transparent PNG files</p><div id="logoPreview" class="preview-section"><div class="preview-label">Preview</div><img id="logoPreviewImg" class="preview-img" src="" alt="Preview"></div><button id="updateLogoBtn"><svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path></svg>Update Company Logo</button><div id="logoStatus" class="status-message"></div></div><!-- Help Section --><div class="image-sources"><h3>📷 Where to get image URLs?</h3><ul><li><a href="https://imgur.com" target="_blank">Imgur.com</a> - Upload → Right-click → Copy image address</li><li><a href="https://postimages.org" target="_blank">Postimages.org</a> - No account needed, instant links</li><li><a href="https://imgbb.com" target="_blank">ImgBB.com</a> - Free hosting with direct links</li><li><strong>Google Drive</strong> - Share link → Change "view" to "uc?export=view&id=FILE_ID"</li><li><strong>Dropbox</strong> - Share link → Change "dl=0" to "dl=1"</li></ul></div></div></div><script>// ============================================// MEMBERSTACK INTEGRATION// ============================================(async function() {const loadingOverlay = document.getElementById('loadingOverlay');loadingOverlay.style.display = 'flex';// Wait for Memberstack to be readyconst memberstack = window.$memberstackDom;if (!memberstack) {alert('Memberstack is not loaded. Please add your Memberstack script to the page.');loadingOverlay.style.display = 'none';return;}try {// Get current memberconst { data: member } = await memberstack.getCurrentMember();if (!member) {alert('Please log in to view and edit your profile');loadingOverlay.style.display = 'none';return;}console.log('Member loaded:', member.id);// Hide loadingloadingOverlay.style.display = 'none';// ============================================// CONFIGURATION// ============================================const CUSTOM_FIELDS = {profilePicture: 'profilePictureUrl',companyLogo: 'companyLogoUrl'};// ============================================// LOAD EXISTING IMAGES// ============================================function loadExistingImages() {const profileUrl = member.customFields?.[CUSTOM_FIELDS.profilePicture];const logoUrl = member.customFields?.[CUSTOM_FIELDS.companyLogo];if (profileUrl) {document.getElementById('profilePictureDisplay').src = profileUrl;document.getElementById('profilePictureUrl').value = profileUrl;}if (logoUrl) {document.getElementById('companyLogoDisplay').src = logoUrl;document.getElementById('companyLogoUrl').value = logoUrl;}}loadExistingImages();// ============================================// LIVE PREVIEW AS USER TYPES// ============================================let profilePreviewTimeout;document.getElementById('profilePictureUrl').addEventListener('input', (e) => {clearTimeout(profilePreviewTimeout);const url = e.target.value.trim();if (url && isValidUrl(url)) {profilePreviewTimeout = setTimeout(() => {const previewSection = document.getElementById('profilePreview');const previewImg = document.getElementById('profilePreviewImg');previewImg.src = url;previewSection.classList.add('show');previewImg.onerror = () => {previewSection.classList.remove('show');};}, 500);} else {document.getElementById('profilePreview').classList.remove('show');}});let logoPreviewTimeout;document.getElementById('companyLogoUrl').addEventListener('input', (e) => {clearTimeout(logoPreviewTimeout);const url = e.target.value.trim();if (url && isValidUrl(url)) {logoPreviewTimeout = setTimeout(() => {const previewSection = document.getElementById('logoPreview');const previewImg = document.getElementById('logoPreviewImg');previewImg.src = url;previewSection.classList.add('show');previewImg.onerror = () => {previewSection.classList.remove('show');};}, 500);} else {document.getElementById('logoPreview').classList.remove('show');}});// ============================================// UPDATE PROFILE PICTURE// ============================================document.getElementById('updateProfileBtn').addEventListener('click', async () => {const btn = document.getElementById('updateProfileBtn');const statusEl = document.getElementById('profileStatus');const urlInput = document.getElementById('profilePictureUrl');const imageUrl = urlInput.value.trim();if (!imageUrl) {showStatus(statusEl, '⚠️ Please enter an image URL', 'error');return;}if (!isValidUrl(imageUrl)) {showStatus(statusEl, '⚠️ Please enter a valid URL (starting with http:// or https://)', 'error');return;}try {btn.disabled = true;btn.innerHTML = '<div class="spinner"></div><span>Updating...</span>';await memberstack.updateMemberJSON({json: {[CUSTOM_FIELDS.profilePicture]: imageUrl}});document.getElementById('profilePictureDisplay').src = imageUrl;showStatus(statusEl, '✅ Profile picture updated successfully!', 'success');// Hide preview after successful updatedocument.getElementById('profilePreview').classList.remove('show');} catch (error) {console.error('Error updating profile picture:', error);showStatus(statusEl, '❌ Failed to update. Please try again.', 'error');} finally {btn.disabled = false;btn.innerHTML = `<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path></svg>Update Profile Picture`;}});// ============================================// UPDATE COMPANY LOGO// ============================================document.getElementById('updateLogoBtn').addEventListener('click', async () => {const btn = document.getElementById('updateLogoBtn');const statusEl = document.getElementById('logoStatus');const urlInput = document.getElementById('companyLogoUrl');const imageUrl = urlInput.value.trim();if (!imageUrl) {showStatus(statusEl, '⚠️ Please enter an image URL', 'error');return;}if (!isValidUrl(imageUrl)) {showStatus(statusEl, '⚠️ Please enter a valid URL (starting with http:// or https://)', 'error');return;}try {btn.disabled = true;btn.innerHTML = '<div class="spinner"></div><span>Updating...</span>';await memberstack.updateMemberJSON({json: {[CUSTOM_FIELDS.companyLogo]: imageUrl}});document.getElementById('companyLogoDisplay').src = imageUrl;showStatus(statusEl, '✅ Company logo updated successfully!', 'success');// Hide preview after successful updatedocument.getElementById('logoPreview').classList.remove('show');} catch (error) {console.error('Error updating company logo:', error);showStatus(statusEl, '❌ Failed to update. Please try again.', 'error');} finally {btn.disabled = false;btn.innerHTML = `<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path></svg>Update Company Logo`;}});// ============================================// HELPER FUNCTIONS// ============================================function showStatus(element, message, type) {element.textContent = message;element.className = `status-message ${type} show`;setTimeout(() => {element.classList.remove('show');}, 5000);}function isValidUrl(string) {try {const url = new URL(string);return url.protocol === 'http:' || url.protocol === 'https:';} catch (_) {return false;}}} catch (error) {console.error('Error loading profile:', error);alert('Error loading profile. Please refresh and try again.');loadingOverlay.style.display = 'none';}})();</script></body></html>I created a complete HTML page that handles everything - the overlapping image display, edit forms, live preview, and auto-save to Memberstack. Users just paste image URLs (from Imgur, Google Drive, etc.) and the system handles the rest.
The overlapping effect looks like this:
I've uploaded the complete code as an artifact above. Just:
YOUR_MEMBERSTACK_IDwith your actual Memberstack IDFeatures included:
✅ Beautiful overlapping display (both images visible)
✅ Live preview as users type URLs
✅ Instant updates to Memberstack
✅ Works without Webflow's file upload field (which requires paid plans)
✅ Fully responsive design
✅ Clear instructions for users on where to get image URLs
To display these images on other pages (like member directories or dashboards), just add images with these attributes:
data-member-profile-picfor profile picturesdata-member-company-logofor company logosAdd this snippet to any page where you want to show the images:
<div style="position: relative; width: 280px; height: 150px; margin: 0 auto;"><img data-member-company-logostyle="position: absolute; left: 0; top: 0; width: 180px; height: 120px;object-fit: contain; background: #f8f9fa; border-radius: 16px;padding: 15px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); border: 4px solid white;"><img data-member-profile-picstyle="position: absolute; right: 0; bottom: 0; width: 120px; height: 120px;object-fit: cover; border-radius: 50%; border: 5px solid white;box-shadow: 0 4px 16px rgba(0,0,0,0.2); z-index: 2;"></div><script>(async function() {const memberstack = window.$memberstackDom;const { data: member } = await memberstack.getCurrentMember();if (member) {document.querySelectorAll('[data-member-profile-pic]').forEach(img => {img.src = member.customFields?.profilePictureUrl || 'https://via.placeholder.com/120?text=Profile';});document.querySelectorAll('[data-member-company-logo]').forEach(img => {img.src = member.customFields?.companyLogoUrl || 'https://via.placeholder.com/180x120?text=Logo';});}})();</script>The script automatically populates them from Memberstack custom fields.
This approach is way simpler than setting up S3 or external storage, and since you mentioned there won't be many updates, storing the URLs in Memberstack custom fields works perfectly!
I've got stuck with CORS (Cross-Origin Resource Sharing) error with the external script -
The error indicates that Webflow is blocking the
https://api.memberstack.com/static/memberstack.jsscript from loading due to CORS restrictions.This typically happens when the external script I'm trying to load is not configured to allow access from certain domains, such as Webflow's
canvas.webflow.com.Hope this helps!
Please sign in to leave a comment.