
How to Use Wix Velo: The Complete Guide to Advanced Web Development on Wix
Introduction to Wix Velo
Wix Velo (formerly Corvid) is a powerful development platform that transforms Wix from a simple website builder into a full-stack development environment. This comprehensive guide will teach you how to use Wix Velo to create dynamic, data-driven websites with custom functionality that goes far beyond standard Wix capabilities.
Whether you're looking to add custom forms, create member areas, build web applications, or integrate third-party services, Wix Velo provides the tools and flexibility you need whilst maintaining the visual design capabilities Wix is known for.
What is Wix Velo?
Wix Velo is an open development platform that allows you to build advanced web applications using JavaScript and Wix's visual editor. It combines the ease of Wix's drag-and-drop interface with the power of code, enabling developers to:
-
Write custom backend and frontend code
-
Access and manipulate databases
-
Create API integrations
-
Build custom user interactions
-
Implement complex business logic
-
Develop full-scale web applications
Key Benefits of Using Wix Velo
-
No Infrastructure Management: Wix handles hosting, security, and scaling
-
Visual Development: Design visually whilst coding functionality
-
Built-in Database: Integrated database management system
-
Serverless Functions: Write backend code without managing servers
-
Extensive APIs: Access to Wix's comprehensive API library
Getting Started with Wix Velo
Step 1: Enable Dev Mode
To begin using Wix Velo, you must first enable Dev Mode in your Wix Editor:
-
Open your Wix site in the Editor
-
Click on "Dev Mode" in the top menu bar
-
Toggle the switch to "On"
-
The code panel will appear at the bottom of your screen
Step 2: Understanding the Velo Interface
Once Dev Mode is enabled, you'll see several new elements:
-
Code Panel: Where you write and edit your code
-
Properties Panel: Shows properties and events for selected elements
-
Site Structure: Displays your site's pages and code files
-
Database Collections: Access to your site's data collections
Step 3: Setting Up Your Development Environment
Before diving into coding, ensure your environment is properly configured:
-
Page Code: Each page can have its own JavaScript file
-
Public Files: For code accessible from any page
-
Backend Files: For server-side code and sensitive operations
-
Packages: NPM packages can be installed for additional functionality Core Concepts in Wix Velo
Working with Page Elements
Every element on your Wix page can be controlled through code. Here's how to interact with page elements:
// Accessing elements by ID $w.onReady(function () { // Get a button element let myButton = $w('#button1'); // Change button text myButton.label = "Click Me!"; // Add click event myButton.onClick(() => { console.log("Button clicked!"); }); });
The $w Selector
The $w selector is fundamental to Wix Velo development. It allows you to:
-
Select page elements
-
Manipulate element properties
-
Attach event handlers
-
Control element visibility
Event Handling
Wix Velo supports various events for different elements:
// Text input events $w('#textInput1').onInput((event) => { let inputValue = event.target.value; // Process input }); // Dropdown selection $w('#dropdown1').onChange((event) => { let selectedValue = event.target.value; // Handle selection });
Working with Databases in Wix Velo
Creating Database Collections
-
Navigate to "Database" in your Wix Dashboard
-
Click "Add Collection"
-
Define your collection structure with fields
-
Set permissions for read/write access
Querying Data
Wix Velo provides a powerful query API:
import wixData from 'wix-data'; // Basic query wixData.query("myCollection") .find() .then((results) => { let items = results.items; // Process results }); // Filtered query wixData.query("myCollection") .eq("status", "active") .ascending("createdDate") .limit(10) .find() .then((results) => { // Handle filtered results });
Inserting and Updating Data
// Insert new item let toInsert = { "title": "New Item", "description": "Item description", "price": 29.99 }; wixData.insert("myCollection", toInsert) .then((item) => { console.log("Item created:", item); }); // Update existing item wixData.update("myCollection", updatedItem) .then((item) => { console.log("Item updated:", item); });
Building Dynamic Pages
Creating Dynamic Item Pages
Dynamic pages allow you to create template pages that populate with database content:
-
Add a dynamic page from the Wix Editor
-
Connect page elements to dataset fields
-
Customise the URL structure
-
Add custom code for additional functionality
Working with Datasets
Datasets act as intermediaries between your database and page elements:
// Filter dataset programmatically $w('#dataset1').setFilter(wixData.filter() .eq("category", "electronics") .gt("price", 100) ); // Refresh dataset $w('#dataset1').refresh();
API Integration and HTTP Functions
Making External API Calls
Wix Velo allows you to integrate with external services:
// Backend code (http-functions.js) import {fetch} from 'wix-fetch'; export async function getExternalData() { const response = await fetch('https://api.example.com/data', { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_TOKEN' } }); return response.json(); }
Creating HTTP Functions
HTTP functions allow external services to communicate with your Wix site:
// http-functions.js import {ok, badRequest} from 'wix-http-functions'; export function post_webhook(request) { return request.body.json() .then((data) => { // Process webhook data return ok({received: true}); }) .catch(() => { return badRequest({error: 'Invalid data'}); }); }
Advanced Wix Velo Features
User Authentication and Members Area
import wixUsers from 'wix-users'; import wixLocation from 'wix-location'; // Check if user is logged in if (wixUsers.currentUser.loggedIn) { // User is logged in let userId = wixUsers.currentUser.id; let userEmail = await wixUsers.currentUser.getEmail(); } // Custom login wixUsers.login(email, password) .then(() => { wixLocation.to("/member-dashboard"); }) .catch((err) => { console.error("Login failed:", err); });
Scheduled Jobs
Create automated tasks that run at specified intervals:
// jobs.config { "jobs": [{ "functionLocation": "/backend/scheduled-jobs.js", "functionName": "dailyReport", "description": "Generate daily reports", "executionConfig": { "cronExpression": "0 9 * * *" } }] }
Working with Wix Stores
Integrate custom functionality with Wix Stores:
import wixStores from 'wix-stores'; // Get current cart wixStores.getCurrentCart() .then((cart) => { console.log("Cart items:", cart.lineItems); }); // Add custom product options export function product_onChange(event) { let selectedOptions = event.target.value; // Custom logic for product variants }
Best Practices for Wix Velo Development
Code Organisation
-
Separate Concerns: Keep frontend and backend logic separate
-
Reusable Functions: Create utility modules for common operations
-
Consistent Naming: Use clear, descriptive names for functions and variables
-
Comment Your Code: Document complex logic and API integrations
Performance Optimisation
-
Minimise Database Queries: Batch operations when possible
-
Use Caching: Store frequently accessed data
-
Lazy Loading: Load content as needed
-
Optimise Images: Use Wix's image optimisation features
Security Considerations
-
Backend Validation: Always validate data on the backend
-
Secure API Keys: Store sensitive data in backend code
-
Permission Management: Set appropriate database permissions
-
Input Sanitisation: Clean user inputs before processing
Common Wix Velo Use Cases
Custom Form Handling
export function submitButton_click(event) { // Validate form fields let email = $w('#emailInput').value; let message = $w('#messageInput').value; if (!email || !message) { $w('#errorText').show(); return; } // Submit to database wixData.insert("ContactSubmissions", { email: email, message: message, submittedAt: new Date() }) .then(() => { $w('#successMessage').show(); $w('#contactForm').hide(); }); }
Dynamic Content Filtering
export function categoryDropdown_change(event) { let selectedCategory = event.target.value; if (selectedCategory === "All") { $w('#dataset1').setFilter(wixData.filter()); } else { $w('#dataset1').setFilter(wixData.filter() .eq("category", selectedCategory) ); } }
Member-Only Content
import wixUsers from 'wix-users'; import wixLocation from 'wix-location'; $w.onReady(function () { if (!wixUsers.currentUser.loggedIn) { wixLocation.to("/login"); } else { // Show member content $w('#memberContent').show(); } });
Troubleshooting Common Issues
Debugging Tips
-
Use Console Logs: Liberal use of console.log() for debugging
-
Preview Mode: Test in preview before publishing
-
Browser DevTools: Inspect network requests and console errors
-
Wix Logs: Check backend logs in the Wix Dashboard
Common Error Solutions
Element Not Found
-
Ensure element IDs match exactly
-
Check that elements exist on the current page
-
Wait for $w.onReady() before accessing elements
Database Permission Errors
-
Verify collection permissions in settings
-
Use backend code for sensitive operations
-
Check user authentication status
API Integration Failures
-
Verify API endpoints and credentials
-
Handle errors gracefully with try-catch
-
Check CORS settings for external APIs
Testing and Deployment
Unit Testing Strategies
Whilst Wix Velo doesn't have built-in testing frameworks, you can implement testing strategies:
// testUtils.js - Backend file export function runTests() { const tests = [ testUserValidation, testPriceCalculation, testDataFormatting ]; let results = []; tests.forEach(test => { try { test(); results.push({name: test.name, status: 'PASSED'}); } catch (error) { results.push({name: test.name, status: 'FAILED', error: error.message}); } }); return results; } function testUserValidation() { const result = validateUserEmail('test@example.com'); if (!result) throw new Error('Valid email failed validation'); }
Staging Environments
Create a staging workflow:
-
Duplicate Your Site: Create a copy for testing
-
Sandbox Database: Use separate collections for testing
-
Environment Variables: Implement environment-specific configurations
// config.js export function getConfig() { const isDev = wixWindow.viewMode === "Preview"; return { apiEndpoint: isDev ? 'https://staging-api.example.com' : 'https://api.example.com', debugMode: isDev }; }
Version Control Integration
Whilst Wix doesn't directly support Git, you can:
-
Export your code regularly
-
Use Wix CLI for local development
-
Maintain a Git repository for code backup
-
Document changes in a changelog
SEO and Performance Monitoring
Velo's Impact on SEO
Wix Velo can enhance or hinder SEO depending on implementation:
// SEO-friendly dynamic page setup import wixSeo from 'wix-seo'; $w.onReady(async function () { // Set dynamic SEO tags const pageData = await getPageData(); wixSeo.setTitle(pageData.title); wixSeo.setMetaDescription(pageData.description); wixSeo.setStructuredData({ "@context": "https://schema.org", "@type": "Article", "headline": pageData.title, "datePublished": pageData.publishDate, "author": { "@type": "Person", "name": pageData.author } }); });
Performance Monitoring
Track and optimise your Velo site's performance:
// Performance tracking export function trackPageLoad() { const loadTime = performance.now(); // Send to analytics wixAnalytics.track('PageLoadTime', { page: wixLocation.path, loadTime: loadTime, device: wixWindow.formFactor }); // Log slow pages if (loadTime > 3000) { console.warn('Slow page load:', loadTime); } }
Core Web Vitals Optimisation
// Lazy load images and content export function setupLazyLoading() { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { loadContent(entry.target); observer.unobserve(entry.target); } }); }); $w('#repeater1').forEachItem(($item) => { observer.observe($item('#lazyImage')[0]); }); }
Advanced Integrations
Payment Processing
Integrate Stripe for custom payment flows:
// Backend - stripe-integration.js import {fetch} from 'wix-fetch'; import {getSecret} from 'wix-secrets-backend'; export async function createPaymentIntent(amount, currency) { const stripeKey = await getSecret('STRIPE_SECRET_KEY'); const response = await fetch('https://api.stripe.com/v1/payment_intents', { method: 'POST', headers: { 'Authorization': `Bearer ${stripeKey}`, 'Content-Type': 'application/x-www-form-urlencoded' }, body: `amount=${amount}¤cy=${currency}` }); return response.json(); }
Email Automation
Create sophisticated email workflows:
import wixCRM from 'wix-crm-backend'; export async function sendAutomatedEmail(contactId, templateId, variables) { try { await wixCRM.emailContact(templateId, contactId, { variables: variables }); // Log email sent await logEmailActivity(contactId, templateId); } catch (error) { console.error('Email failed:', error); } } // Triggered email example export async function afterInsert(item, context) { // Send welcome email when new member signs up if (context.collectionName === 'Members') { await sendAutomatedEmail(item._id, 'welcome-template', { name: item.firstName, membershipLevel: item.level }); } }
Social Media Integration
Connect with social platforms:
// Twitter/X API integration export async function postToTwitter(message) { const twitterApi = await getTwitterClient(); try { const tweet = await twitterApi.post('tweets', { text: message, media: { media_ids: await uploadMedia() } }); return tweet.data; } catch (error) { console.error('Twitter post failed:', error); } } // Instagram Basic Display API export async function getInstagramFeed(accessToken) { const response = await fetch( `https://graph.instagram.com/me/media?fields=id,caption,media_url&access_token=${accessToken}` ); return response.json(); }
Webhook Implementation
Handle incoming webhooks securely:
// http-functions.js import crypto from 'crypto'; import {getSecret} from 'wix-secrets-backend'; export async function post_webhook(request) { // Verify webhook signature const signature = request.headers['x-webhook-signature']; const secret = await getSecret('WEBHOOK_SECRET'); const body = await request.body.text(); const expectedSignature = crypto .createHmac('sha256', secret) .update(body) .digest('hex'); if (signature !== expectedSignature) { return badRequest({error: 'Invalid signature'}); } // Process webhook const data = JSON.parse(body); await processWebhookData(data); return ok({received: true}); }
Real-World Project Examples
Building a Booking System
Complete booking system implementation:
// booking-system.js import wixData from 'wix-data'; import wixUsers from 'wix-users'; export class BookingSystem { constructor() { this.collectionName = 'Bookings'; this.slotsCollection = 'TimeSlots'; } async getAvailableSlots(date, serviceId) { const startOfDay = new Date(date); startOfDay.setHours(0, 0, 0, 0); const endOfDay = new Date(date); endOfDay.setHours(23, 59, 59, 999); const bookedSlots = await wixData.query(this.collectionName) .between('dateTime', startOfDay, endOfDay) .eq('serviceId', serviceId) .eq('status', 'confirmed') .find(); const allSlots = await this.generateTimeSlots(date, serviceId); return allSlots.filter(slot => !this.isSlotBooked(slot, bookedSlots.items)); } async createBooking(slotData) { const user = wixUsers.currentUser; const userId = user.id; // Check availability again const isAvailable = await this.checkSlotAvailability(slotData); if (!isAvailable) { throw new Error('Slot no longer available'); } const booking = { userId: userId, serviceId: slotData.serviceId, dateTime: slotData.dateTime, duration: slotData.duration, status: 'pending', createdAt: new Date() }; const result = await wixData.insert(this.collectionName, booking); // Send confirmation email await this.sendBookingConfirmation(result); return result; } } // Frontend implementation $w.onReady(function () { const bookingSystem = new BookingSystem(); $w('#datePicker').onChange(async (event) => { const selectedDate = event.target.value; const serviceId = $w('#serviceDropdown').value; const availableSlots = await bookingSystem.getAvailableSlots(selectedDate, serviceId); $w('#timeSlotsRepeater').data = availableSlots; }); });
Creating a Custom CRM
Build a customer relationship management system:
// crm-backend.js export class CRMSystem { async createContact(contactData) { // Validate and sanitise data const validatedData = this.validateContactData(contactData); // Check for duplicates const existing = await wixData.query('Contacts') .eq('email', validatedData.email) .find(); if (existing.items.length > 0) { return this.updateContact(existing.items[0]._id, validatedData); } // Add metadata validatedData.createdAt = new Date(); validatedData.lastActivity = new Date(); validatedData.score = this.calculateLeadScore(validatedData); return wixData.insert('Contacts', validatedData); } calculateLeadScore(contact) { let score = 0; // Score based on engagement if (contact.emailOpens > 5) score += 20; if (contact.websiteVisits > 10) score += 30; if (contact.downloadedContent) score += 25; if (contact.requestedDemo) score += 50; return Math.min(score, 100); } async trackActivity(contactId, activityType, details) { const activity = { contactId: contactId, type: activityType, details: details, timestamp: new Date() }; await wixData.insert('Activities', activity); // Update last activity await wixData.update('Contacts', { _id: contactId, lastActivity: new Date() }); } }
Developing a Membership Portal
Create a comprehensive membership system:
// membership-portal.js import wixUsers from 'wix-users'; import wixPay from 'wix-pay'; import wixData from 'wix-data'; export class MembershipPortal { async setupMemberDashboard() { const user = wixUsers.currentUser; if (!user.loggedIn) { wixLocation.to('/login'); return; } const memberData = await this.getMemberData(user.id); this.displayMemberInfo(memberData); this.loadMemberContent(memberData.level); } async getMemberData(userId) { const member = await wixData.query('Members') .eq('userId', userId) .include('subscriptionHistory') .find(); return member.items[0]; } async upgradeMembership(newLevel) { const payment = await wixPay.createPayment({ amount: this.getLevelPrice(newLevel), currency: 'GBP', description: `Upgrade to ${newLevel} membership` }); const result = await payment.startPayment(); if (result.status === 'Successful') { await this.updateMemberLevel(newLevel); } } async loadMemberContent(level) { const content = await wixData.query('MemberContent') .hasSome('allowedLevels', [level, 'all']) .ascending('publishDate') .find(); $w('#contentRepeater').data = content.items; } }
E-Learning Platform Example
Build an online course platform:
// e-learning-platform.js export class LearningPlatform { async enrollInCourse(courseId) { const user = wixUsers.currentUser; const enrollment = { userId: user.id, courseId: courseId, enrolledDate: new Date(), progress: 0, completedModules: [], status: 'active' }; await wixData.insert('Enrollments', enrollment); // Grant access to course content await this.grantCourseAccess(user.id, courseId); } async trackProgress(courseId, moduleId, completed) { const enrollment = await this.getEnrollment(courseId); if (completed && !enrollment.completedModules.includes(moduleId)) { enrollment.completedModules.push(moduleId); enrollment.progress = this.calculateProgress(courseId, enrollment.completedModules); await wixData.update('Enrollments', enrollment); if (enrollment.progress === 100) { await this.issueCertificate(enrollment); } } } async getNextLesson(courseId) { const enrollment = await this.getEnrollment(courseId); const course = await this.getCourse(courseId); const incompleteLessons = course.modules.filter( module => !enrollment.completedModules.includes(module._id) ); return incompleteLessons[0] || null; } }
Mobile Development
Responsive Design with Velo
Optimise for mobile devices:
import wixWindow from 'wix-window'; $w.onReady(function () { // Detect device type if (wixWindow.formFactor === "Mobile") { setupMobileInterface(); } else if (wixWindow.formFactor === "Tablet") { setupTabletInterface(); } else { setupDesktopInterface(); } // Handle orientation changes wixWindow.addEventListener('orientationchange', () => { adjustLayoutForOrientation(); }); }); function setupMobileInterface() { // Mobile-specific adjustments $w('#mobileMenu').show(); $w('#desktopMenu').hide(); // Simplify complex interactions $w('#dataTable').hide(); $w('#mobileCardView').show(); // Adjust touch targets $w('.button').style.minHeight = "44px"; } function adjustLayoutForOrientation() { const isLandscape = window.orientation === 90 || window.orientation === -90; if (isLandscape) { $w('#header').collapse(); } else { $w('#header').expand(); } }
Touch Gestures and Mobile Interactions
// Swipe navigation for mobile galleries export function setupSwipeGallery() { let touchStartX = 0; let touchEndX = 0; $w('#galleryContainer').onMouseIn((event) => { if (wixWindow.formFactor !== "Mobile") return; event.target.addEventListener('touchstart', (e) => { touchStartX = e.changedTouches[0].screenX; }); event.target.addEventListener('touchend', (e) => { touchEndX = e.changedTouches[0].screenX; handleSwipe(); }); }); function handleSwipe() { if (touchEndX < touchStartX - 50) { // Swipe left - next image $w('#gallery').next(); } if (touchEndX > touchStartX + 50) { // Swipe right - previous image $w('#gallery').previous(); } } }
Limitations and Workarounds
NPM Package Limitations
Wix Velo has restrictions on npm packages:
Allowed Package Types:
-
Pure JavaScript packages
-
Packages without native dependencies
-
Packages under 10MB
Workarounds:
// If a package isn't available, implement functionality manually // Example: Simple UUID generator when uuid package isn't available export function generateUUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); }
Memory and Execution Limits
Handle Velo's execution constraints:
// Backend functions have 14-second timeout export async function processLargeDataset(datasetId) { const BATCH_SIZE = 100; let offset = 0; let hasMore = true; while (hasMore) { const batch = await wixData.query('LargeCollection') .skip(offset) .limit(BATCH_SIZE) .find(); // Process batch await processBatch(batch.items); offset += BATCH_SIZE; hasMore = batch.hasNext(); // Prevent timeout by breaking large operations if (offset > 1000) { // Schedule continuation await scheduleNextBatch(datasetId, offset); break; } } }
File Storage Restrictions
Work within Velo's file limitations:
// Media Manager API for file handling import wixMediaManager from 'wix-media-manager'; export async function uploadAndProcessFile(fileInfo) { // File size limit: 25MB for most file types if (fileInfo.size > 25 * 1024 * 1024) { throw new Error('File too large. Maximum size is 25MB'); } // Upload to Media Manager const uploadResult = await wixMediaManager.upload( fileInfo.fileUrl, { fileName: fileInfo.name, labels: ['user-upload', 'processed'] } ); return uploadResult; }
Third-Party Script Limitations
Navigate script restrictions:
// Load external scripts properly export function loadExternalScript(src) { return new Promise((resolve, reject) => { // Scripts must be HTTPS if (!src.startsWith('https://')) { reject(new Error('Scripts must use HTTPS')); return; } const script = document.createElement('script'); script.src = src; script.onload = resolve; script.onerror = reject; // Add to head document.head.appendChild(script); }); }
GDPR and Privacy Compliance
Cookie Consent Implementation
Build GDPR-compliant cookie consent:
import wixWindow from 'wix-window'; import {local} from 'wix-storage'; export function setupCookieConsent() { // Check if consent already given const consentGiven = local.getItem('cookieConsent'); if (!consentGiven) { $w('#cookieBanner').show(); } $w('#acceptCookies').onClick(() => { local.setItem('cookieConsent', 'accepted'); local.setItem('consentDate', new Date().toISOString()); $w('#cookieBanner').hide(); // Enable analytics enableAnalytics(); }); $w('#rejectCookies').onClick(() => { local.setItem('cookieConsent', 'rejected'); $w('#cookieBanner').hide(); // Disable non-essential cookies disableNonEssentialCookies(); }); } function enableAnalytics() { // Enable Google Analytics or other tracking if (typeof gtag !== 'undefined') { gtag('consent', 'update', { 'analytics_storage': 'granted' }); } }
Data Protection with Velo
Implement data protection features:
// User data export functionality export async function exportUserData(userId) { const userData = {}; // Collect data from various collections const collections = ['Members', 'Orders', 'Activities', 'Preferences']; for (const collection of collections) { const data = await wixData.query(collection) .eq('userId', userId) .find(); userData[collection] = data.items; } // Create downloadable file const jsonData = JSON.stringify(userData, null, 2); const blob = new Blob([jsonData], {type: 'application/json'}); return { data: userData, downloadUrl: URL.createObjectURL(blob) }; } // Data deletion export async function deleteUserData(userId) { // Verify user identity const currentUser = wixUsers.currentUser; if (currentUser.id !== userId) { throw new Error('Unauthorised'); } // Delete from all collections const collections = ['Members', 'Orders', 'Activities']; for (const collection of collections) { await wixData.query(collection) .eq('userId', userId) .find() .then(results => { const deletePromises = results.items.map(item => wixData.remove(collection, item._id) ); return Promise.all(deletePromises); }); } // Delete user account await wixUsers.deleteUser(); }
Privacy-First Analytics
Implement privacy-respecting analytics:
// Anonymous analytics tracking export function trackEvent(eventName, parameters) { // Check consent const consent = local.getItem('cookieConsent'); if (consent !== 'accepted') { // Only track essential, anonymous data const anonymousEvent = { event: eventName, timestamp: new Date(), // No user-identifiable information sessionId: generateSessionId(), pageUrl: wixLocation.path }; // Store locally or send to privacy-focused service sendAnonymousAnalytics(anonymousEvent); } else { // Full analytics with user consent wixAnalytics.track(eventName, parameters); } }
Advanced Animation and Interactions
Timeline Animations
Create sophisticated animations:
import wixAnimations from 'wix-animations'; export function createTimelineAnimation() { const timeline = wixAnimations.timeline(); timeline .add($w('#element1'), { duration: 1000, opacity: 1, y: 0, scale: 1 }) .add($w('#element2'), { duration: 800, opacity: 1, x: 0, rotate: 360 }, '-=400') // Start 400ms before previous ends .add($w('#element3'), { duration: 600, opacity: 1, scale: 1.2, easing: 'easeOutBounce' }); return timeline; } // Trigger animation on scroll export function setupScrollAnimation() { let animated = false; $w('#animationTrigger').onViewportEnter(() => { if (!animated) { animated = true; const timeline = createTimelineAnimation(); timeline.play(); } }); }
Scroll-Triggered Effects
Implement parallax and scroll effects:
export function setupParallaxEffect() { let lastScrollY = 0; wixWindow.addEventListener('scroll', () => { const scrollY = wixWindow.scrollY; const speed = 0.5; // Parallax speed // Background parallax $w('#backgroundImage').style.transform = `translateY(${scrollY * speed}px)`; // Fade in elements $w('.fadeInElement').forEach(element => { const elementTop = element.getBoundingClientRect().top; const windowHeight = wixWindow.innerHeight; if (elementTop < windowHeight * 0.8) { element.show('fade', {duration: 1000}); } }); // Update progress bar updateScrollProgress(scrollY); lastScrollY = scrollY; }); } function updateScrollProgress(scrollY) { const docHeight = document.body.scrollHeight - wixWindow.innerHeight; const progress = (scrollY / docHeight) * 100; $w('#progressBar').style.width = `${progress}%`; }
Custom Loading States
Create engaging loading experiences:
export class LoadingManager { constructor() { this.loadingElement = $w('#customLoader'); this.progressBar = $w('#loadingProgress'); this.loadingText = $w('#loadingText'); } async showLoader(loadingSteps) { this.loadingElement.show(); let currentStep = 0; for (const step of loadingSteps) { this.loadingText.text = step.message; // Execute step await step.action(); // Update progress currentStep++; const progress = (currentStep / loadingSteps.length) * 100; this.progressBar.style.width = `${progress}%`; // Smooth animation await this.delay(300); } await this.hideLoader(); } async hideLoader() { await this.loadingElement.hide('fade', {duration: 500}); } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } // Usage const loader = new LoadingManager(); await loader.showLoader([ { message: 'Loading user data...', action: () => loadUserProfile() }, { message: 'Fetching content...', action: () => loadDynamicContent() }, { message: 'Preparing dashboard...', action: () => setupDashboard() } ]);
Interactive UI Components
Build custom interactive components:
export class InteractiveCard { constructor(cardElement) { this.card = cardElement; this.isFlipped = false; this.setupInteractions(); } setupInteractions() { // 3D flip effect this.card.onMouseIn(() => { this.card.style.transform = 'scale(1.05)'; this.card.style.boxShadow = '0 10px 30px rgba(0,0,0,0.3)'; }); this.card.onMouseOut(() => { this.card.style.transform = 'scale(1)'; this.card.style.boxShadow = '0 5px 15px rgba(0,0,0,0.1)'; }); this.card.onClick(() => { this.flipCard(); }); } flipCard() { if (this.isFlipped) { this.card.style.transform = 'rotateY(0deg)'; $w('#cardFront').show(); $w('#cardBack').hide(); } else { this.card.style.transform = 'rotateY(180deg)'; $w('#cardFront').hide(); $w('#cardBack').show(); } this.isFlipped = !this.isFlipped; } }
Conclusion
Wix Velo transforms Wix from a simple website builder into a powerful development platform. By combining visual design with custom code, you can create sophisticated web applications whilst leveraging Wix's infrastructure and tools.
Start with simple customisations and gradually build your expertise. The combination of Wix's visual editor and Velo's coding capabilities offers unlimited possibilities for creating unique, functional websites that meet specific business requirements.
Remember to follow best practices, keep security in mind, and leverage the extensive Wix Velo API documentation as you develop. With practice and experimentation, you'll be building complex web applications on Wix in no time.
Additional Resources
Start your Wix Velo journey today and unlock the full potential of your Wix website!