When optimizing an e-commerce store for speed and performance, reducing unused JavaScript is one of the quickest ways to improve Core Web Vitals. If you have custom functionality meant solely for single product pages—such as custom gallery sliders, sizing calculators, or custom variation selectors—you want to ensure that code never runs on the shop archive, checkout, or homepage.

The Short Answer: Yes, You Are Doing It Right

Using is_product() inside the wp_enqueue_scripts action hook is the official, canonical WordPress and WooCommerce method to conditionally load assets on single product pages. Your approach is largely correct, but there are a few defensive coding practices and modern WordPress standards you should apply to make it bulletproof.

The Recommended Implementation

Here is an enhanced, production-ready version of your snippet:

function my_custom_enqueue_product_script() {
    // 1. Safety check: ensure WooCommerce is active and function exists
    if ( ! function_exists( 'is_product' ) ) {
        return;
    }

    // 2. Check if we are viewing a single product page
    if ( is_product() ) {
        wp_enqueue_script(
            'custom-product-script',
            get_stylesheet_directory_uri() . '/js/product.js',
            array( 'jquery' ),
            '1.0.0',
            array(
                'in_footer' => true,
                'strategy'  => 'defer', // Available since WordPress 6.3
            )
        );
    }
}
add_action( 'wp_enqueue_scripts', 'my_custom_enqueue_product_script' );

Key Enhancements Explained

1. Guard Against Fatal Errors

Wrapping your conditional with function_exists( 'is_product' ) prevents fatal errors if WooCommerce is deactivated, updating, or running in an isolated environment. Without this check, WordPress could throw an uncaught Error: Call to undefined function is_product().

2. Modern Script Loading Strategies

Starting with WordPress 6.3, you can pass an array to the final parameter of wp_enqueue_script(). Specifying 'strategy' => 'defer' ensures the browser does not block DOM construction while downloading your script, directly improving your Largest Contentful Paint (LCP) and First Contentful Paint (FCP) metrics.

Common Conditional Tags in WooCommerce

It helps to know where is_product() stops and other WooCommerce conditionals begin:

  • is_product(): Returns true only on single product detail pages.
  • is_shop(): Returns true on the main shop archive page.
  • is_product_category(): Returns true on category archive pages.
  • is_woocommerce(): Returns true across any WooCommerce-generated page (shop, product, category, tag).

Bonus: Target Specific Products Only

If your script is resource-heavy and only required for specific types of items (such as variable products or a single product ID), you can refine the condition even further:

if ( is_product() ) {
    global $product;

    // Only load for Variable Products
    if ( $product && $product->is_type( 'variable' ) ) {
        wp_enqueue_script( 'variable-product-helper', ... );
    }
}

Conclusion

Your original logic was spot-on. By adding a wrapper to confirm WooCommerce is active and leveraging deferred loading, you keep your site secure against PHP errors and ensure top-tier loading speeds for your shoppers.