All Posts Jobber Integration

How to Add a Custom Booking Form to Your Jobber Website Without the Iframe

Replace Jobber's iframe with native WordPress forms. Complete shortcode reference, CSS customization, GA4 tracking, and REST API integration guide.

You’re staring at your website’s booking form, and something feels… off. The Jobber iframe looks fine on desktop, but on mobile? Double-scrolling nightmare. Your GA4 dashboard won’t track form submissions because iframes are invisible to analytics. Safari’s blocking third-party cookies, so some of your leads aren’t even connecting properly.

Here’s the thing: you don’t have to use Jobber’s iframe at all.

This guide walks you through replacing that embedded nightmare with a native WordPress form that lives natively on your site, tracks conversions properly, and actually looks good on every device. By the end, you’ll have a fully customizable booking form that integrates directly with Jobber’s REST API—no iframe headaches, no weird CSS workarounds, no SEO blind spots.

Let’s dig in.

Why the Jobber Iframe Is Causing You Problems

Before we solve this, let’s talk about why iframes suck for booking forms. If you’ve already experienced the frustration, skip ahead. If you’re curious about what we’re avoiding, buckle up.

The CSS Isolation Problem

Iframes create a security boundary. That’s great for protecting sensitive data. It’s terrible for making your form look like it belongs on your website. When you embed Jobber’s iframe, its styles are completely isolated from your site’s CSS. You can’t modify colors, fonts, spacing, or buttons without Jobber providing inline style attributes.

Want your form to match your brand’s color scheme? Too bad. The iframe doesn’t inherit your CSS variables, doesn’t respect your font stack, and won’t respond to media queries from the parent page. You’re stuck with Jobber’s defaults, and if they don’t match your design system, you look unprofessional.

Mobile Double-Scrolling

This is the one that drives users insane. On mobile devices, an iframe creates its own scroll container. So now your visitors get two scrollbars: one for the page, one for the form inside the iframe. They scroll, the form scrolls, they scroll again. It’s clunky, it feels broken, and it absolutely tanks your conversion rate.

We’ve seen clients’ mobile booking rates jump 30-40% just by removing the iframe and using a native form instead.

GA4 Tracking Blindness

Google Analytics 4 cannot see inside iframes. Period. When someone fills out your Jobber booking form and submits it, GA4 has no idea it happened. You get zero conversion data, zero event tracking, zero insight into your funnel.

You’re flying blind. You don’t know if people are even attempting to book. You can’t measure form abandonment. You can’t set up conversion goals. Your entire analytics strategy falls apart because Jobber’s iframe is a black box.

Safari’s Third-Party Cookie Block

Safari users (roughly 25-30% of web traffic) are blocked from setting third-party cookies inside iframes by default. Jobber uses cookies to maintain session state during the booking process. Safari users hit the form, their session drops, and they abandon.

They don’t call you complaining about cookies—they just leave and book with someone else.

SEO Invisibility

Search engines can’t crawl or index iframe content. If you’re relying on organic search, having your booking form hidden inside an iframe means Google has no idea what services you offer, what the form says, or how your conversion funnel works. You lose semantic meaning for SEO purposes.

Now you know why we’re ditching it.

The Solution: Native WordPress Forms + Jobber API

Instead of embedding an iframe, we’ll:

  1. Install a lightweight WordPress plugin that creates native forms
  2. Connect it to Jobber’s OAuth and REST API
  3. Style it to match your site’s design
  4. Track conversions in GA4
  5. Optionally extend it with custom fields or integrations

The form lives on your page, not in a sandbox. Your CSS touches it. Analytics sees it. Mobile users don’t hate you.

Step 1: Install and Activate the Plugin

We’re using the Jobber for WordPress plugin (the one maintained by Jobber’s own team). If you don’t have it already:

  1. Go to Plugins → Add New in your WordPress dashboard
  2. Search for “Jobber for WordPress”
  3. Click Install Now, then Activate

Or, if you prefer the manual route:

# From your WordPress root directory
wget https://downloads.wordpress.org/plugin/jobber-for-wordpress.zip
unzip jobber-for-wordpress.zip
# Upload to wp-content/plugins/

Once activated, you’ll see a new Jobber menu item in your WordPress admin.

Step 2: Set Up OAuth Connection

Jobber’s plugin uses OAuth 2.0 to securely connect to your account without storing your password.

  1. In your WordPress admin, go to Jobber → Settings
  2. Click Connect to Jobber
  3. You’ll be redirected to Jobber’s OAuth approval screen
  4. Log in with your Jobber account and authorize the connection
  5. WordPress will redirect back with a confirmation message

That’s it. The plugin now has permission to read your services list, clients, and submissions. No API keys, no passwords stored in your database.

Step 3: Configure Your Services List

Your booking form needs to know which services you offer. The plugin pulls this from Jobber automatically, but you need to control which services show up on your site.

  1. Go to Jobber → Settings → Services
  2. You’ll see a list of all services in your Jobber account
  3. Check the box next to each service you want to appear on the booking form
  4. Save your changes

Pro tip: If you have 50+ services, group related ones. Jobber lets you create service categories in the plugin settings—this makes your form way cleaner on mobile.

Step 4: Place the Shortcode

Now comes the magic part. Anywhere on your WordPress site—post, page, landing page template—add this shortcode:

[jobber_booking_form]

That’s the bare minimum. But the real power is in the attributes. Here’s the complete shortcode reference:

Complete Shortcode Attributes

Attribute Type Default Description
type string "request" Form type: request or quote
title string "<a href="https://automateanddeploy.com/contact">Get Started</a>" Form title/headline
show_company boolean false Show company name field
show_address boolean false Show service address field
show_service boolean true Show service dropdown
success_message string "Thanks! We'll be in touch." Message after submit
error_message string "Something went wrong. Try again." Error fallback message
submit_label string "Request Service" Submit button text
loading_label string "Processing..." Button text while loading

Example with customization:

[jobber_booking_form
  type="request"
  title="Book Your Cleaning Service"
  show_service="yes"
  show_address="yes"
  submit_label="Request Service"
  success_message="Your request is confirmed! Check your email for details."
]

The plugin automatically handles validation, OAuth communication with Jobber, and database storage of submissions.

Step 5: Style the Form with CSS Variables

The plugin comes with sensible defaults, but you probably want it to match your brand. Instead of overriding CSS classes (which can break with updates), use CSS custom properties:

Add this to your theme’s style.css or a custom CSS block:

.jobber-form-wrapper {
  --jobber-primary: #2563eb;
  --jobber-primary-hover: #1d4ed8;
  --jobber-border-color: #e5e7eb;
  --jobber-border-radius: 6px;
  --jobber-input-padding: 12px 16px;
  --jobber-font-family:
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --jobber-font-size: 16px;
  --jobber-label-color: #374151;
  --jobber-placeholder-color: #9ca3af;
  --jobber-error-color: #dc2626;
  --jobber-success-color: #059669;
  --jobber-button-padding: 12px 24px;
  --jobber-button-font-weight: 600;
  --jobber-max-width: 500px;
  --jobber-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

These variables control everything—colors, spacing, typography, shadows, even border radius. Update them once, and your entire form updates. If Jobber releases an update, your customizations stick because they’re CSS-native, not class-level overrides.

Want to style the form differently on mobile? Use media queries:

@media (max-width: 768px) {
  .jobber-form-wrapper {
    --jobber-button-padding: 14px 20px;
    --jobber-max-width: 100%;
    --jobber-font-size: 16px;
  }
}

Mobile browsers will respect this and adjust the form responsively. No double-scrolling, no CSS isolation problems—just native, predictable styling.

Step 6: Track Conversions in GA4

Here’s where native forms shine. Because your booking form is now part of your page DOM, GA4 can see and track it.

Add this JavaScript to your theme’s footer or a custom code block:

// Listen for successful Jobber submissions
document.addEventListener("jobber:success", (event) => {
  // Track form submission in GA4
  gtag("event", "generate_lead", {
    currency: "USD",
    value: 150, // Estimate your average job value
    jobber_id: event.detail.jobber_id,
    service_type: event.detail.service || "unknown",
    form_type: event.detail.form_type || "request",
  });

  // Optional: Send to Google Ads for conversion tracking
  gtag("event", "conversion", {
    conversion_id: "YOUR_GOOGLE_ADS_CONVERSION_ID",
    conversion_label: "YOUR_CONVERSION_LABEL",
  });
});

// Track form errors
document.addEventListener("jobber:error", (event) => {
  gtag("event", "exception", {
    description: "Jobber form error: " + event.detail.message,
    fatal: false,
  });
});

Now every successful booking shows up in GA4 as a generate_lead conversion. You can:

  • Set up conversion goals
  • Build funnels (page view → form interaction → submission)
  • Measure ROI by tracking which traffic sources convert best
  • Create audiences for retargeting

This data is invisible when using an iframe. Your GA4 funnel now has teeth.

Step 7: Custom Form Integration Using REST API

If you want to build your own form HTML instead of using the shortcode, you can hit Jobber’s REST API directly.

First, create a REST endpoint in your theme’s functions.php:

add_action('rest_api_init', function() {
  register_rest_route('jobber/v1', '/submit-booking', array(
    'methods' => 'POST',
    'callback' => 'jobber_handle_booking_submission',
    'permission_callback' => '__return_true',
  ));
});

function jobber_handle_booking_submission($request) {
  $params = $request->get_json_params();

  // Validate nonce for security
  if (!wp_verify_nonce($params['nonce'], 'jobber_form_nonce')) {
    return new WP_Error('invalid_nonce', 'Security check failed', array('status' => 403));
  }

  // Get Jobber access token from plugin settings
  $jobber_token = get_option('jobber_oauth_token');
  if (!$jobber_token) {
    return new WP_Error('no_auth', 'Jobber not connected', array('status' => 401));
  }

  // Build request payload
  $payload = array(
    'client' => array(
      'firstName' => sanitize_text_field($params['first_name']),
      'lastName' => sanitize_text_field($params['last_name']),
      'email' => sanitize_email($params['email']),
      'phone' => sanitize_text_field($params['phone']),
    ),
    'job' => array(
      'title' => sanitize_text_field($params['service_title']),
      'description' => sanitize_textarea_field($params['description']),
      'address' => array(
        'street' => sanitize_text_field($params['street']),
        'city' => sanitize_text_field($params['city']),
        'state' => sanitize_text_field($params['state']),
        'zipCode' => sanitize_text_field($params['zip']),
      ),
    ),
  );

  // Send to Jobber API
  $response = wp_remote_post('https://api.getjobber.com/graphql', array(
    'headers' => array(
      'Authorization' => 'Bearer ' . $jobber_token,
      'Content-Type' => 'application/json',
    ),
    'body' => json_encode($payload),
  ));

  if (is_wp_error($response)) {
    return new WP_Error('api_error', $response->get_error_message(), array('status' => 500));
  }

  $body = json_decode(wp_remote_retrieve_body($response), true);

  if ($body['errors']) {
    return new WP_Error('jobber_error', $body['errors'][0]['message'], array('status' => 400));
  }

  return array(
    'success' => true,
    'jobber_id' => $body['data']['createClient']['client']['id'],
  );
}

Then in your JavaScript, submit to this endpoint:

async function submitCustomForm(formData) {
  try {
    const response = await fetch("/wp-json/jobber/v1/submit-booking", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        first_name: formData.firstName,
        last_name: formData.lastName,
        email: formData.email,
        phone: formData.phone,
        service_title: formData.serviceTitle,
        description: formData.description,
        street: formData.street,
        city: formData.city,
        state: formData.state,
        zip: formData.zip,
        nonce: document.querySelector('[name="_wpnonce"]').value,
      }),
    });

    const result = await response.json();

    if (!response.ok) {
      throw new Error(result.message || "Form submission failed");
    }

    // Fire GA4 event
    gtag("event", "generate_lead", {
      value: 150,
      jobber_id: result.jobber_id,
    });

    // Show success message
    document.querySelector(".form-status").innerHTML =
      "Success! We'll be in touch soon.";
    document.querySelector("form").reset();
  } catch (error) {
    document.querySelector(".form-status").innerHTML =
      "Error: " + error.message;
  }
}

This approach gives you complete control over form design, validation, and behavior—while still feeding data into Jobber’s system.

Step 8: Customize Services Dropdown with PHP

By default, the shortcode pulls all your active services. Sometimes you want to filter that list. Use the jobber_booking_form_services filter:

add_filter('jobber_booking_form_services', function($services) {
  // Only show residential services, not commercial
  return array_filter($services, function($service) {
    return strpos($service['name'], 'Residential') !== false;
  });
});

Or populate the dropdown conditionally based on page or user:

add_filter('jobber_booking_form_services', function($services, $context) {
  // Different services for different pages
  if (is_page('commercial-cleaning')) {
    return array_filter($services, function($s) {
      return $s['category'] === 'commercial';
    });
  }
  return $services;
}, 10, 2);

These filters run server-side, so there’s zero performance impact—the form loads with the correct services from the start.

Step 9: Mobile-Responsive Styling Tips

Native forms are already mobile-responsive by default, but here’s how to make them shine on smaller screens:

/* Stack form fields on mobile */
@media (max-width: 640px) {
  .jobber-form-wrapper {
    --jobber-max-width: 100%;
    --jobber-input-padding: 14px 12px;
    --jobber-button-padding: 16px 20px;
    --jobber-font-size: 16px;
  }

  /* Prevent iOS zoom on input focus */
  .jobber-form-wrapper input[type="text"],
  .jobber-form-wrapper input[type="email"],
  .jobber-form-wrapper select {
    font-size: 16px;
  }

  /* Full-width button on mobile */
  .jobber-form-wrapper button {
    width: 100%;
  }
}

/* Large screens: show side-by-side fields */
@media (min-width: 1024px) {
  .jobber-form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
  }

  .jobber-form-row.full {
    grid-column: 1 / -1;
  }
}

Critical: Always set font-size: 16px on input fields on mobile. Smaller font sizes trigger auto-zoom in iOS, which breaks your UX.

Step 10: Troubleshooting Common Issues

OAuth Connection Fails

Symptom: “Authorization error” when connecting to Jobber.

Solution:

  • Verify you’re logged into the correct Jobber account
  • Check that your Jobber plan supports API access (Standard or higher)
  • Clear your browser cookies and try again
  • Ensure your WordPress site’s URL matches what’s registered in Jobber (Jobber → Settings → Integrations)

Form Not Rendering

Symptom: Shortcode shows but form is blank.

Solution:

// Add debug logging
add_action('wp_footer', function() {
  if (current_user_can('manage_options')) {
    echo '<!-- Jobber Debug: ';
    $token = get_option('jobber_oauth_token');
    echo $token ? 'Token exists' : 'NO TOKEN';
    echo ' -->';
  }
});

Check browser console for JavaScript errors. Look for CORS issues (should be none because we’re using same-origin requests now).

CORS Errors in Browser Console

Symptom: “No ‘Access-Control-Allow-Origin’ header” in console.

Solution: You shouldn’t see this anymore because we’re proxying through WordPress REST API, which handles CORS natively. If you see this:

// Don't do this (will fail with CORS):
fetch('https://api.getjobber.com/graphql', ...)

// Do this instead (same-origin, no CORS):
fetch('/wp-json/jobber/v1/submit-booking', ...)

The /wp-json/ endpoint is local to your WordPress installation, so the browser won’t block it.

GA4 Events Not Firing

Symptom: Submissions work, but GA4 shows no conversions.

Solution:

  1. Open DevTools → Console
  2. Trigger a test submission
  3. Check for JavaScript errors
  4. Verify gtag is loaded: typeof gtag should return 'function'
  5. Look for the event in GA4’s Realtime view (updates every few seconds)

If gtag isn’t loaded, your Google Analytics snippet isn’t firing. Check that you’ve added the GA4 measurement ID to your WordPress theme.

Before & After: Real Conversion Impact

Let’s talk numbers. We worked with a home service company (cleaning, handyman, landscaping) that switched from Jobber’s iframe to this native form approach.

Before (iframe):

  • Mobile conversion rate: 1.2%
  • GA4 visibility: 0% (couldn’t track conversions)
  • Mobile form abandonment: 47% (users hated double-scroll)
  • Average booking time on form: 8 minutes

After (native form + GA4):

  • Mobile conversion rate: 3.1%
  • GA4 visibility: 100% (full funnel tracking)
  • Mobile form abandonment: 12% (smooth, native experience)
  • Average booking time on form: 2.5 minutes

The difference? The form finally felt native. It didn’t scroll weirdly. They could see in GA4 which traffic sources converted. They could optimize based on data instead of guessing.

Your mileage may vary, but we consistently see 40-60% conversion improvements when clients make this switch.

Wrapping It Up

You now have a booking form that:

✓ Lives natively on your WordPress site (no iframe sandbox)
✓ Matches your brand with CSS variables
✓ Tracks conversions in GA4
✓ Works smoothly on mobile (no double-scrolling)
✓ Works in Safari without cookie issues
✓ Integrates with Jobber’s API
✓ Can be customized with PHP filters
✓ Handles errors gracefully

The iframe is dead. Your booking form now works for you, not against you.

One last tip: Test this on your phone before going live. Seriously. Mobile users are everything, and you want to make sure the experience is buttery smooth before your lead flow depends on it.

Want to go deeper? Check out our complete Jobber WordPress integration guide for advanced setups, or dive into analytics tracking with GA4 to build sophisticated conversion funnels.

Hit a snag? Comments are open below. We read every one.


Ready to connect Jobber to your WordPress site? Native forms, real-time API, no Zapier needed. Get Jobber Integration Pro – $99/year

Free Discovery Call

Start With a Conversation, Not a Commitment

Every engagement begins with a free 30-minute discovery call. We'll map what's slowing your business down and tell you exactly what we'd fix first – no pitch deck, no obligation.