Industry Guide

Tattoo Studio Booking Form: Build Tattoo Appointment Request Forms

February 2026 · 11 min read

Tattoo requests through Instagram DMs and email are chaotic. "Hey, can you do a tattoo for me?" tells you nothing. A proper booking form collects everything upfront: design ideas, size, placement, style preferences, and availability. Here's how to build one that saves time and sets expectations.

The challenge with tattoo bookings is that every project is different. A tiny finger tattoo and a full back piece require completely different information. The form needs to adapt based on what the client wants, showing relevant questions while hiding ones that don't apply.

We'll build a booking form that handles new tattoos, cover-ups, touch-ups, and consultations - with conditional logic that tailors the experience for each.

Service Type Selection

Start by understanding what the client needs. A new tattoo, cover-up, touch-up, or just a consultation? Each has different information requirements. A touch-up of existing work doesn't need a design description - they already have the tattoo.

const serviceSection = form.addSubform('service', {
    title: 'What Are You Looking For?'
});

serviceSection.addRow(row => {
    row.addRadioButton('serviceType', {
        label: 'Service Type',
        options: [
            { id: 'new-tattoo', name: 'New Tattoo' },
            { id: 'coverup', name: 'Cover-Up' },
            { id: 'touchup', name: 'Touch-Up (existing work)' },
            { id: 'consultation', name: 'Consultation Only' }
        ],
        orientation: 'vertical',
        isRequired: true
    });
});

serviceSection.addRow(row => {
    row.addCheckbox('isFirstTattoo', {
        label: 'This will be my first tattoo',
        isVisible: () => serviceType.value() === 'new-tattoo'
    });
});

The "first tattoo" checkbox helps manage expectations. First-timers often underestimate pain and overestimate what they can handle in one session. It's useful context for the artist.

Tattoo Style Preferences

Style matters enormously in tattooing. Different artists specialize in different styles. A realism specialist and a traditional artist produce very different work. Collecting style preferences helps match clients with the right artist.

const styleSection = form.addSubform('style', {
    title: 'Tattoo Style',
    isVisible: () => serviceType.value() !== 'consultation'
});

styleSection.addRow(row => {
    row.addSuggestionChips('tattooStyle', {
        label: 'Preferred Style(s)',
        suggestions: [
            { id: 'traditional', name: 'Traditional/Old School' },
            { id: 'neo-traditional', name: 'Neo-Traditional' },
            { id: 'realism', name: 'Realism' },
            { id: 'blackwork', name: 'Blackwork' },
            { id: 'fineline', name: 'Fine Line' },
            { id: 'watercolor', name: 'Watercolor' },
            { id: 'japanese', name: 'Japanese' },
            { id: 'geometric', name: 'Geometric' },
            { id: 'tribal', name: 'Tribal' },
            { id: 'lettering', name: 'Script/Lettering' }
        ],
        min: 1
    });
});

styleSection.addRow(row => {
    row.addRadioButton('colorPreference', {
        label: 'Color Preference',
        options: [
            { id: 'black-grey', name: 'Black & Grey' },
            { id: 'color', name: 'Full Color' },
            { id: 'mixed', name: 'Mix of Both' },
            { id: 'undecided', name: 'Not sure yet' }
        ],
        orientation: 'horizontal'
    });
});

Suggestion chips work better than a dropdown for styles because clients often want multiple related styles. Someone might want "fine line" and "geometric" together, or "traditional" with "Japanese" influences.

Pro tip

Color preference affects pricing and session length. Full color typically takes longer than black and grey. Capturing this upfront helps with scheduling and estimates.

Size and Placement

Size determines price and session length. Placement affects difficulty and pain level. Ribs and feet hurt more than arms. These details help artists prepare clients for what to expect.

const detailsSection = form.addSubform('details', {
    title: 'Size & Placement',
    isVisible: () => serviceType.value() !== 'consultation'
});

detailsSection.addRow(row => {
    row.addDropdown('size', {
        label: 'Approximate Size',
        options: [
            { id: 'tiny', name: 'Tiny (< 2 inches)' },
            { id: 'small', name: 'Small (2-4 inches)' },
            { id: 'medium', name: 'Medium (4-6 inches)' },
            { id: 'large', name: 'Large (6-10 inches)' },
            { id: 'xlarge', name: 'Extra Large (10+ inches)' },
            { id: 'sleeve', name: 'Half/Full Sleeve' },
            { id: 'back-piece', name: 'Back Piece' }
        ],
        isRequired: true
    });
});

detailsSection.addRow(row => {
    row.addDropdown('placement', {
        label: 'Body Placement',
        options: [
            { id: 'forearm', name: 'Forearm' },
            { id: 'upper-arm', name: 'Upper Arm/Shoulder' },
            { id: 'wrist', name: 'Wrist' },
            { id: 'hand', name: 'Hand/Fingers' },
            { id: 'chest', name: 'Chest' },
            { id: 'back', name: 'Back' },
            { id: 'ribs', name: 'Ribs/Side' },
            { id: 'thigh', name: 'Thigh' },
            { id: 'calf', name: 'Calf' },
            { id: 'ankle', name: 'Ankle/Foot' },
            { id: 'neck', name: 'Neck' },
            { id: 'other', name: 'Other' }
        ],
        isRequired: true
    });
});

// Cover-up specific question
detailsSection.addRow(row => {
    row.addDropdown('existingTattooSize', {
        label: 'Size of Existing Tattoo to Cover',
        options: [
            { id: 'small', name: 'Small (< 3 inches)' },
            { id: 'medium', name: 'Medium (3-5 inches)' },
            { id: 'large', name: 'Large (5+ inches)' }
        ],
        isVisible: () => serviceType.value() === 'coverup',
        isRequired: () => serviceType.value() === 'coverup'
    });
});

For cover-ups, you need to know how big the existing tattoo is. Cover-ups typically need to be larger than the original to properly hide it. The conditional question only appears for cover-up requests.

Design Description

Here's where clients describe what they want. A text area for their vision, plus questions about reference images. This is the creative brief that artists will work from.

const designSection = form.addSubform('design', {
    title: 'Design Details',
    isVisible: () => serviceType.value() !== 'consultation' &&
                     serviceType.value() !== 'touchup'
});

designSection.addRow(row => {
    row.addTextarea('designDescription', {
        label: 'Describe Your Idea',
        placeholder: 'Tell us about your design concept, any meaningful elements, symbols, or themes you want to include...',
        rows: 4,
        isRequired: true
    });
});

designSection.addRow(row => {
    row.addRadioButton('hasReference', {
        label: 'Do you have reference images?',
        options: [
            { id: 'yes', name: 'Yes, I have reference images' },
            { id: 'no', name: 'No, starting from scratch' },
            { id: 'partial', name: 'Some ideas, need artist input' }
        ],
        orientation: 'vertical'
    });
});

// Reference upload guidance
designSection.addRow(row => {
    row.addTextPanel('referenceNote', {
        computedValue: () => 'Please email reference images to studio@example.com with your name in the subject line.',
        isVisible: () => hasReference.value() === 'yes' || hasReference.value() === 'partial',
        customStyles: {
            'font-style': 'italic',
            'color': '#666'
        }
    });
});

The reference image question determines follow-up. If they have references, tell them where to send them. If they're starting from scratch, the artist knows they'll be creating from the description alone.

Artist Selection

Most studios have multiple artists with different specialties. Let clients express preferences while leaving room for flexibility if their preferred artist isn't available.

const artistSection = form.addSubform('artist', {
    title: 'Artist Preference'
});

artistSection.addRow(row => {
    row.addDropdown('preferredArtist', {
        label: 'Preferred Artist',
        options: [
            { id: 'any', name: 'No preference - match me with someone' },
            { id: 'mike', name: 'Mike - Traditional/Neo-trad' },
            { id: 'sarah', name: 'Sarah - Fine Line/Minimalist' },
            { id: 'jake', name: 'Jake - Realism/Portraits' },
            { id: 'elena', name: 'Elena - Blackwork/Geometric' },
            { id: 'owner', name: 'Shop Owner - Japanese/Large Scale' }
        ]
    });
});

artistSection.addRow(row => {
    row.addCheckbox('flexibleArtist', {
        label: 'I am flexible on artist if my preferred artist is unavailable',
        isVisible: () => preferredArtist.value() !== 'any'
    });
});

"Match me with someone" is the most common selection. It lets the studio assign based on style match and availability. The flexibility checkbox for specific preferences helps with scheduling.

Real-Time Pricing

Showing estimated pricing manages expectations. Clients often have no idea what tattoos cost. A "starting at" price based on size and style helps them understand the investment before the consultation.

// Pricing calculation based on size and complexity
const basePrices: Record<string, { min: number; hourly: number }> = {
    'tiny': { min: 80, hourly: 0 },     // Flat rate
    'small': { min: 150, hourly: 0 },   // Flat rate
    'medium': { min: 250, hourly: 175 },
    'large': { min: 400, hourly: 175 },
    'xlarge': { min: 600, hourly: 175 },
    'sleeve': { min: 1500, hourly: 175 },
    'back-piece': { min: 2000, hourly: 175 }
};

const styleMultipliers: Record<string, number> = {
    'realism': 1.3,
    'japanese': 1.2,
    'watercolor': 1.15,
    'traditional': 1.0,
    'neo-traditional': 1.1,
    'blackwork': 1.0,
    'fineline': 1.0,
    'geometric': 1.05,
    'tribal': 1.0,
    'lettering': 0.9
};

const colorMultiplier = form.computedValue(() => {
    const color = colorPreference.value();
    if (color === 'color') return 1.15;
    if (color === 'mixed') return 1.1;
    return 1.0;
});

const estimatedCost = form.computedValue(() => {
    const sizeKey = size.value();
    if (!sizeKey) return null;

    const pricing = basePrices[sizeKey];
    if (!pricing) return null;

    const styles = tattooStyle.value() ?? [];
    const maxStyleMultiplier = Math.max(
        ...styles.map(s => styleMultipliers[s] ?? 1.0),
        1.0
    );

    const base = pricing.min;
    const adjusted = base * maxStyleMultiplier * colorMultiplier();

    // Cover-ups add premium
    const coverupPremium = serviceType.value() === 'coverup' ? 1.25 : 1.0;

    return Math.round(adjusted * coverupPremium);
});

const pricingSection = form.addSubform('pricing', {
    title: 'Estimated Cost',
    isVisible: () => serviceType.value() !== 'consultation' && size.value() !== null
});

pricingSection.addRow(row => {
    row.addPriceDisplay('estimate', {
        label: 'Starting At',
        computedValue: estimatedCost,
        currency: '$',
        decimals: 0,
        variant: 'highlight'
    });
});

pricingSection.addRow(row => {
    row.addTextPanel('pricingNote', {
        computedValue: () => 'Final price determined during consultation based on design complexity and session time required.',
        customStyles: {
            'font-size': '13px',
            'color': '#666'
        }
    });
});

Style and color multipliers reflect the reality that some work is more complex. Realism takes longer than traditional. Full color takes longer than black and grey. The math captures these differences.

See more booking form examples in our gallery.

Deposit Requirements

Deposits protect against no-shows. They're standard in the industry, but clients don't always expect them. Show the deposit amount upfront with clear policy language.

const depositSection = form.addSubform('deposit', {
    title: 'Deposit Information',
    isVisible: () => serviceType.value() !== 'consultation'
});

const depositAmount = form.computedValue(() => {
    const sizeKey = size.value();
    if (sizeKey === 'sleeve' || sizeKey === 'back-piece') return 200;
    if (sizeKey === 'large' || sizeKey === 'xlarge') return 100;
    return 50;
});

depositSection.addRow(row => {
    row.addPriceDisplay('deposit', {
        label: 'Required Deposit',
        computedValue: depositAmount,
        currency: '$',
        decimals: 0
    });
});

depositSection.addRow(row => {
    row.addTextPanel('depositPolicy', {
        computedValue: () => 'Deposit is non-refundable but applies to your final tattoo cost. Cancellations with less than 48 hours notice forfeit deposit.'
    });
});

Larger pieces require larger deposits because they block more time. A no-show on a 6-hour back piece session hurts more than a no-show on a small flash piece.

Availability and Scheduling

Matching client availability with artist schedules is the final puzzle. Collect which days work, preferred times, and how urgently they want to get tattooed.

const scheduleSection = form.addSubform('schedule', {
    title: 'Availability'
});

scheduleSection.addRow(row => {
    row.addCheckboxList('availableDays', {
        label: 'Days You Are Available',
        options: [
            { id: 'monday', name: 'Monday' },
            { id: 'tuesday', name: 'Tuesday' },
            { id: 'wednesday', name: 'Wednesday' },
            { id: 'thursday', name: 'Thursday' },
            { id: 'friday', name: 'Friday' },
            { id: 'saturday', name: 'Saturday' }
        ],
        orientation: 'horizontal',
        min: 1
    });
});

scheduleSection.addRow(row => {
    row.addDropdown('preferredTime', {
        label: 'Preferred Time of Day',
        options: [
            { id: 'morning', name: 'Morning (10am-1pm)' },
            { id: 'afternoon', name: 'Afternoon (1pm-5pm)' },
            { id: 'evening', name: 'Evening (5pm-8pm)' },
            { id: 'flexible', name: 'Flexible' }
        ]
    });
});

scheduleSection.addRow(row => {
    row.addDropdown('timeline', {
        label: 'When are you looking to get tattooed?',
        options: [
            { id: 'asap', name: 'As soon as possible' },
            { id: '2-weeks', name: 'Within 2 weeks' },
            { id: '1-month', name: 'Within 1 month' },
            { id: 'flexible', name: 'Flexible on timing' },
            { id: 'specific-date', name: 'I have a specific date in mind' }
        ]
    });
});

scheduleSection.addRow(row => {
    row.addDatepicker('targetDate', {
        label: 'Target Date',
        isVisible: () => timeline.value() === 'specific-date',
        minDate: () => new Date().toISOString()
    });
});

The timeline question helps prioritize. "As soon as possible" gets contacted first. "Flexible on timing" can wait for the perfect artist availability.

Contact Information

Finally, collect how to reach them. Age verification is mandatory - studios can't tattoo minors. The "how did you hear about us" question helps track marketing effectiveness.

const contactSection = form.addSubform('contact', {
    title: 'Contact Information'
});

contactSection.addRow(row => {
    row.addTextbox('fullName', {
        label: 'Full Name',
        isRequired: true
    });
    row.addEmail('email', {
        label: 'Email',
        isRequired: true
    });
});

contactSection.addRow(row => {
    row.addTextbox('phone', {
        label: 'Phone',
        isRequired: true,
        pattern: '^[0-9\-\+\(\)\s]+$'
    });
    row.addInteger('age', {
        label: 'Age',
        min: 18,
        max: 100,
        isRequired: true,
        tooltip: 'Must be 18+ to get tattooed'
    });
});

contactSection.addRow(row => {
    row.addDropdown('hearAboutUs', {
        label: 'How did you hear about us?',
        options: [
            { id: 'instagram', name: 'Instagram' },
            { id: 'google', name: 'Google Search' },
            { id: 'referral', name: 'Friend/Family Referral' },
            { id: 'walk-by', name: 'Walked by the shop' },
            { id: 'other', name: 'Other' }
        ]
    });
});

Phone and email both matter. Phone for quick scheduling confirmations, email for sending reference images and design previews back and forth.

Health and Safety Questions

Some studios add health screening to the booking form. Allergies, medications, and medical conditions can affect tattooing. Better to know before the appointment.

const healthSection = form.addSubform('health', {
    title: 'Health Information'
});

healthSection.addRow(row => {
    row.addRadioButton('hasAllergies', {
        label: 'Any known allergies to latex, adhesives, or metals?',
        options: [
            { id: 'no', name: 'No' },
            { id: 'yes', name: 'Yes' }
        ],
        orientation: 'horizontal'
    });
});

healthSection.addRow(row => {
    row.addTextbox('allergyDetails', {
        label: 'Please describe your allergies',
        isVisible: () => hasAllergies.value() === 'yes',
        isRequired: () => hasAllergies.value() === 'yes'
    });
});

healthSection.addRow(row => {
    row.addRadioButton('hasMedicalConditions', {
        label: 'Any medical conditions that affect healing? (diabetes, blood disorders, etc.)',
        options: [
            { id: 'no', name: 'No' },
            { id: 'yes', name: 'Yes' }
        ],
        orientation: 'horizontal'
    });
});

These questions are optional for the form but required knowledge before tattooing. Collect them during booking to avoid day-of surprises.

Flash vs Custom Pricing

Flash tattoos (pre-drawn designs) have fixed prices. Custom work is hourly or project-based. If your studio offers flash, add a toggle that changes the pricing logic entirely.

serviceSection.addRow(row => {
    row.addRadioButton('designType', {
        label: 'Design Type',
        options: [
            { id: 'custom', name: 'Custom Design' },
            { id: 'flash', name: 'Flash (from our designs)' },
            { id: 'bring-own', name: 'Bring My Own Design' }
        ],
        orientation: 'vertical',
        isVisible: () => serviceType.value() === 'new-tattoo'
    });
});

// Flash selection (would show available flash designs)
serviceSection.addRow(row => {
    row.addDropdown('flashSelection', {
        label: 'Select Flash Design',
        options: [
            { id: 'flash-001', name: 'Rose - $150' },
            { id: 'flash-002', name: 'Dagger - $180' },
            { id: 'flash-003', name: 'Snake - $200' },
            { id: 'flash-004', name: 'Skull - $175' }
        ],
        isVisible: () => designType.value() === 'flash'
    });
});

Flash pricing is flat - no size or style calculations needed. The conditional logic keeps the form simple for flash clients while still collecting all the details for custom work.

Putting It Together

A complete tattoo booking form guides clients through service type, style preferences, size and placement, design description, artist selection, and scheduling - while calculating estimated costs and deposits. The form adapts based on answers, showing only relevant questions for each type of request.

The goal is to arrive at the consultation with everything documented. No more "what did we discuss in DMs?" or "I thought we said something different." It's all in the form submission, ready to reference.

Common Questions

Should I require reference images in the form?

Make them optional but encouraged. Some clients have no references and that's fine - they want the artist's vision. Others have Pinterest boards full of ideas. Accommodate both by asking if they have references, then providing upload instructions for those who do.

How do I handle underage inquiries?

The age field with min: 18 prevents underage submissions. You could also add a disclaimer that parental consent forms are required for 16-17 year olds if your jurisdiction allows tattooing minors with consent.

Should pricing be shown or hidden?

Show 'starting at' prices based on size. It manages expectations and filters out people who aren't prepared to pay fair rates. Just make clear that final pricing is determined during consultation after seeing the actual design complexity.

How do I handle walk-ins vs appointments?

This form is for appointments. Walk-ins are handled differently - they see what's available that day. Consider a separate simpler form for walk-in requests if you want to manage those online too.

Ready to Build Your Tattoo Booking Form?

Stop managing bookings through DMs. Create a form that captures everything upfront.