Skip to content
Checkout

WooCommerce Checkout Notices & Thank-You Page: A Practical Customization Guide

· · 12 min read
Quote card: WooCommerce checkout notices should use validation hooks to block orders, or display hooks for informational messages

Getting WooCommerce checkout notices to appear in the right place, and at the right moment, trips up more developers than it should. A thank-you page that says nothing useful after a customer just paid you money is the same problem in reverse: a small technical detail with an outsized effect on trust and support volume.

WooCommerce gives you the hooks to fix both. This guide walks through WooCommerce checkout notices and the order-received page end to end, with working code for each pattern, so you can add the right message in the right place without guessing.

WooCommerce checkout notices should use validation hooks to block orders, or display hooks for informational messages

Why Checkout Notices and the Thank-You Page Matter for Conversions

Checkout is the highest-stakes screen in a WooCommerce store. Every notice a shopper sees there either builds confidence (shipping cutoff times, a security reassurance, a stock warning) or creates friction (a validation error with no context, a generic “invalid field” message).

The thank-you page carries similar weight in the other direction: it’s the first thing a customer sees after handing over their card details, and a blank, templated “Thank you, your order has been received” line is a missed opportunity to confirm next steps, cross-sell, or reduce a “did that actually work?” support ticket.

Both surfaces are controlled by well-documented WooCommerce hooks. The trick is knowing which hook fires at which point in the request lifecycle, because firing a notice too early or too late either breaks the checkout flow or silently does nothing.

Understanding the WooCommerce Checkout Hook Stack

Before writing any code, it helps to know the order of operations on the checkout page. WooCommerce renders the checkout form through a defined sequence of action hooks (the same underlying WordPress hooks mechanism that powers most plugin and theme customization), and notices you add have to target the right one depending on whether you want the message to show before the form loads, during validation, or after an order is placed.

Where woocommerce_before_checkout_form Fires

woocommerce_before_checkout_form runs right before WooCommerce outputs the checkout form markup, on the standard checkout page template.

It’s the hook most theme and plugin developers reach for when they want to show a static or conditional message above the billing and shipping fields, things like a delivery cutoff notice, a minimum order value warning, or a reminder about accepted payment methods. Because it runs on every page load (not just on form submission), it’s the right place for informational notices, not validation errors.

The wc_add_notice() Function Explained

wc_add_notice( $message, $notice_type, $data ) is the function WooCommerce itself uses to queue up notices for display, documented in the official WooCommerce code reference. It accepts three notice types: success, notice, and error. Notices queued with this function are stored in the WooCommerce session and rendered wherever wc_print_notices() is called, which on checkout is typically near the top of the form.

The key thing to understand is that wc_add_notice() doesn’t render anything by itself; it queues a message that gets flushed on the next relevant page load or AJAX response, which is exactly why it pairs well with validation hooks like woocommerce_checkout_process and woocommerce_after_checkout_validation.

How to Add WooCommerce Checkout Notices

With the hook order in mind, here are the four patterns that cover almost every checkout notice request we see from client teams.

A Simple Informational Notice Example

The most common request is a static banner above the checkout form, for example a shipping cutoff reminder. This uses woocommerce_before_checkout_form directly, since it’s purely informational and doesn’t depend on cart state.

add_action( 'woocommerce_before_checkout_form', 'wcd_checkout_cutoff_notice', 5 );
function wcd_checkout_cutoff_notice() {
    $cutoff_hour = 14; // 2 PM local time
    $current_hour = (int) current_time( 'G' );

    if ( $current_hour < $cutoff_hour ) {
        echo '<div class="woocommerce-info wcd-cutoff-notice">Order in the next '
            . esc_html( $cutoff_hour - $current_hour )
            . ' hours to qualify for same-day dispatch.</div>';
    } else {
        echo '<div class="woocommerce-info wcd-cutoff-notice">Orders placed after 2 PM ship the next business day.</div>';
    }
}

Note the priority of 5, ahead of the default 10. If you want the notice above other plugins’ checkout messaging (coupon forms, login prompts), an earlier priority keeps it at the top.

Conditional Notices Based on Cart Contents

A more useful pattern checks the cart itself, for example flagging a low-stock item or a product that requires a longer lead time. This still hooks into woocommerce_before_checkout_form, but reads from WC()->cart.

add_action( 'woocommerce_before_checkout_form', 'wcd_preorder_lead_time_notice', 8 );
function wcd_preorder_lead_time_notice() {
    if ( WC()->cart->is_empty() ) {
        return;
    }

    foreach ( WC()->cart->get_cart() as $cart_item ) {
        $product_id = $cart_item['product_id'];

        if ( get_post_meta( $product_id, '_wcd_preorder', true ) === 'yes' ) {
            wc_print_notice(
                sprintf(
                    /* translators: %s: product name */
                    __( '%s is a pre-order item and ships in 2-3 weeks. The rest of your order will ship on the standard schedule.', 'wcd' ),
                    get_the_title( $product_id )
                ),
                'notice'
            );
            break; // one notice is enough even with multiple pre-order items
        }
    }
}

Using wc_print_notice() here (rather than raw HTML) keeps the markup consistent with WooCommerce’s own styling, which matters if the active theme has customized the .woocommerce-notice classes.

Validation Notices with woocommerce_checkout_process

When a notice needs to actually block checkout, that’s a validation problem, not a display problem, and it belongs on woocommerce_checkout_process combined with wc_add_notice() at type error. This hook fires after the customer submits the form but before the order is created, and any error-type notice added here stops the order from processing.

add_action( 'woocommerce_checkout_process', 'wcd_require_purchase_order_number' );
function wcd_require_purchase_order_number() {
    // Only enforce this for logged-in wholesale customers.
    if ( ! is_user_logged_in() || ! current_user_can( 'wholesale_customer' ) ) {
        return;
    }

    if ( empty( $_POST['wcd_po_number'] ) ) {
        wc_add_notice( __( 'A purchase order number is required for wholesale checkout.', 'wcd' ), 'error' );
    }
}

This is the pattern that trips people up most often: adding an error notice on woocommerce_before_checkout_form instead of a validation hook. It will display the text, but it will not stop the order, because that hook doesn’t run during form processing at all.

Field-Specific Error Notices

For notices tied to a specific field, woocommerce_after_checkout_validation gives access to both the submitted data array and the WP_Error object WooCommerce uses internally, which lets you attach the message to the exact field instead of a generic banner.

add_action( 'woocommerce_after_checkout_validation', 'wcd_validate_phone_format', 10, 2 );
function wcd_validate_phone_format( $data, $errors ) {
    $phone = $data['billing_phone'] ?? '';

    if ( $phone && ! preg_match( '/^\+?[0-9\s\-\(\)]{7,}$/', $phone ) ) {
        $errors->add( 'validation', __( 'Please enter a valid phone number so we can reach you about delivery.', 'wcd' ) );
    }
}

Notice Placement Hooks at a Glance

HookFires WhenBest For
woocommerce_before_checkout_formPage load, before form markupStatic or cart-based informational notices
woocommerce_checkout_processOn submit, before order creationCustom fields or business-rule validation that should block checkout
woocommerce_after_checkout_validationDuring validation, with data + errors objectsField-specific validation tied to submitted values
woocommerce_review_order_before_paymentInside the order review tablePayment-method-specific notices
woocommerce_before_order_notesJust above the order notes fieldNotices about gift messages or delivery instructions

If a notice needs to stop the order, it belongs on a validation hook with wc_add_notice( $message, 'error' ). If it’s purely informational, keep it on a display hook. Mixing the two up is the single most common cause of “my checkout notice doesn’t do anything” bug reports.


Customizing the WooCommerce Thank-You (Order-Received) Page

Once an order is placed, WooCommerce redirects to the order-received endpoint, generally /checkout/order-received/{order_id}/. This page pulls from the same thankyou.php template used across every WooCommerce install, and everything on it, the confirmation heading, the order details table, the “thank you” copy, is filterable or hookable without touching template files directly.

Where WooCommerce Renders the Thank-You Message

The core thank-you text comes from WC_Order::get_checkout_order_received_text(), which is filtered through woocommerce_thankyou_order_received_text. This is the correct entry point for changing the message itself, rather than editing the template file, since it survives WooCommerce core and theme updates.

add_filter( 'woocommerce_thankyou_order_received_text', 'wcd_custom_thankyou_text', 10, 2 );
function wcd_custom_thankyou_text( $text, $order ) {
    if ( ! $order ) {
        return $text;
    }

    $first_name = $order->get_billing_first_name();

    return sprintf(
        /* translators: %s: customer first name */
        __( 'Thanks, %s! Your order is confirmed and a receipt is on its way to your inbox. Questions about delivery? Reply to that email any time.', 'wcd' ),
        esc_html( $first_name )
    );
}

Adding Custom Content to the Order-Received Page

To add content rather than replace it, for example a “what happens next” block, a support link, or a related-products upsell, hook into woocommerce_thankyou, which passes the order ID and fires after the standard order details table.

add_action( 'woocommerce_thankyou', 'wcd_thankyou_next_steps_block', 20 );
function wcd_thankyou_next_steps_block( $order_id ) {
    if ( ! $order_id ) {
        return;
    }

    $order = wc_get_order( $order_id );

    if ( ! $order || ! $order->needs_processing() ) {
        return; // Skip for orders that don't need fulfillment, e.g. failed or refunded.
    }

    echo '<div class="wcd-next-steps">';
    echo '<h3>' . esc_html__( 'What happens next', 'wcd' ) . '</h3>';
    echo '<ol>';
    echo '<li>' . esc_html__( 'We pack your order within one business day.', 'wcd' ) . '</li>';
    echo '<li>' . esc_html__( 'You get a tracking email as soon as it ships.', 'wcd' ) . '</li>';
    echo '<li>' . esc_html__( 'Delivery typically takes 3-5 business days.', 'wcd' ) . '</li>';
    echo '</ol>';
    echo '</div>';
}

Note the $order_id check and the needs_processing() guard. woocommerce_thankyou also fires for orders that failed payment or landed in a status that shouldn’t show fulfillment messaging, so gating on order state avoids showing “we’re packing your order” copy on a failed payment.

Redirecting to a Custom Thank-You Page

Some stores need more control than the default template allows, a fully custom landing page with its own layout, tracking pixels, or a multi-step post-purchase flow. The clean way to do this is woocommerce_thankyou_order_id combined with a redirect, rather than editing thankyou.php in a child theme, which is fragile against core updates and doesn’t handle order-specific data as cleanly.

add_action( 'template_redirect', 'wcd_redirect_to_custom_thankyou' );
function wcd_redirect_to_custom_thankyou() {
    if ( ! is_wc_endpoint_url( 'order-received' ) ) {
        return;
    }

    global $wp;
    $order_id = absint( $wp->query_vars['order-received'] ?? 0 );

    if ( ! $order_id ) {
        return;
    }

    $order = wc_get_order( $order_id );

    if ( ! $order || ! $order->get_order_key() || $order->get_order_key() !== ( $_GET['key'] ?? '' ) ) {
        return; // Never redirect without verifying the order key.
    }

    $custom_page_url = add_query_arg(
        array(
            'order_id'  => $order_id,
            'order_key' => $order->get_order_key(),
        ),
        home_url( '/order-confirmed/' )
    );

    wp_safe_redirect( $custom_page_url );
    exit;
}

The order key check is not optional. Order-received URLs are guessable if you only rely on the numeric ID, so any custom thank-you page needs to validate order_key against the order before displaying anything sensitive, exactly the way WooCommerce core does internally in the WC_Order class reference.


Plugin vs Custom Snippet: Which Approach Fits Your Store

Everything above can be done with a lightweight custom snippet loaded from a small site-specific plugin. But not every store should go that route, especially if you’d rather manage notice copy through a settings screen; in that case a roundup like our dedicated cart and checkout notice plugins guide is a better starting point than a snippet. Here’s the decision most teams end up making, based on what we see across client builds.

ScenarioRecommended ApproachWhy
One or two static notices, no conditional logicCustom snippetA plugin is overhead for something a 15-line function handles fully
Notices need a non-technical admin UI (marketing team edits copy)PluginSnippets require a developer for every text change; a settings page doesn’t
Conditional notices based on cart rules, user roles, or geolocationCustom snippet, if the logic is store-specificOff-the-shelf plugins rarely match bespoke business rules exactly, and you end up fighting the plugin’s assumptions
Thank-you page redirect with order-key validation and multi-step post-purchase flowCustom developmentThis touches security-sensitive logic (order key checks, endpoint handling) that benefits from code review, not a settings toggle
A/B testing thank-you page variants or notice copyPlugin or custom, depending on scaleAt low volume a plugin’s built-in testing is fine; at scale, custom integration with an analytics stack usually wins

The general rule: if the requirement is “add this message here,” write the snippet, it’s faster to build, faster to review, and has no ongoing plugin-update surface area.

If the requirement is “let someone on the marketing team change this without a developer,” or “this needs to interact with other systems (CRM, inventory, fraud checks) on the checkout and thank-you pages,” that’s usually where a small custom plugin, or a scoped engagement with a WooCommerce development team, pays for itself. We build both kinds of solutions for clients regularly, and the honest answer is usually “start with the snippet, and only formalize it into a plugin once the requirements outgrow a single function.”


Testing and Debugging Checkout Customizations

A few practical checks before shipping any of the code above to production:

  • Test with both a guest checkout and a logged-in account, notices scoped to is_user_logged_in() or specific roles behave differently between the two.
  • Test the block-based checkout separately from the classic shortcode checkout if the store has migrated. The hooks in this guide target the classic checkout template; if you’re planning or mid-way through migrating to the Checkout block, the block-based checkout uses a different extension API based on ExperimentalOrderMeta slots and store API filters.
  • Use WP_DEBUG plus a query monitor plugin to confirm your hook actually fires. It’s easy to attach a validation notice to the wrong priority and have another plugin’s checkout_process hook short-circuit before yours runs.
  • Check the notice output on mobile viewports. Custom HTML added via echo inside a notice hook inherits the theme’s .woocommerce-notice styling only if you use the correct CSS classes; raw <div> tags without them often render unstyled.
  • For the redirect pattern, confirm the destination page still has access to order data through the passed order_id and order_key, and that it degrades gracefully if someone lands there without those parameters.

When to Bring in a WooCommerce Developer

Most of what’s covered here is well within reach for a developer comfortable with WordPress hooks and basic PHP.

Where it gets harder is when checkout notices or the thank-you flow need to talk to other systems, syncing order status to a CRM before the confirmation renders, running fraud checks that gate which notices display, or building a multi-step post-purchase page with its own analytics and personalization. That’s the point where a few isolated snippets tend to turn into a maintenance burden, and it’s usually worth having a WooCommerce-focused developer scope the full flow once, rather than patching hooks one request at a time.


Frequently Asked Questions

Where do you set the thank-you message in WooCommerce?

The default thank-you message comes from WC_Order::get_checkout_order_received_text(), and the correct way to change it is the woocommerce_thankyou_order_received_text filter shown above, not editing thankyou.php directly in a child theme. Filtering keeps the change intact through WooCommerce and theme updates.

How do I add a notice to the WooCommerce checkout page without a plugin?

Hook a function onto woocommerce_before_checkout_form for informational notices, or use wc_add_notice() on woocommerce_checkout_process for anything that needs to validate and potentially block the order. Both work from a small snippet in a site-specific plugin; no dedicated notice plugin is required for straightforward cases.

Can I redirect customers to a completely custom thank-you page?

Yes, using template_redirect combined with WooCommerce’s order-received endpoint detection, as shown in the redirect example above. The important detail most examples online skip is validating order_key against the order before redirecting or displaying any order data, since the order-received URL is not secret on its own.

Do these hooks work with the WooCommerce block-based checkout?

Not directly. The hooks in this guide target the classic, shortcode-based checkout template. The newer Checkout block uses a separate extension system built around ExperimentalOrderMeta slot fills and Store API filters, which is a different integration path even though the underlying business logic (validation rules, notice copy) carries over conceptually.


Key Takeaways

Checkout notices and the thank-you page both run on a small, well-defined set of hooks once you know which one does what. Informational notices belong on display hooks like woocommerce_before_checkout_form. Anything that should block an order belongs on woocommerce_checkout_process or woocommerce_after_checkout_validation with wc_add_notice() at the error type.

The thank-you page’s text is filterable through woocommerce_thankyou_order_received_text, additional content hangs off woocommerce_thankyou, and a full custom redirect needs an order-key check before it touches anything sensitive. Start with a snippet for anything narrow and store-specific; reach for a plugin or a scoped development engagement once non-developers need to manage the copy or the flow needs to integrate with other systems.

If your checkout or thank-you page needs deeper customization than a snippet can comfortably handle, that’s exactly the kind of scoped WooCommerce development work our team takes on, get in touch and we’ll help you map out the right approach for your store.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *