Template Overrides & Hooks
Change how service cards, vendor profiles, dashboards, and order screens look -- through your theme, without touching plugin files.
The plugin ships 98 templates: 47 frontend templates plus 51 email templates. Any of them can be overridden, and most of them also expose hooks so you can add markup without overriding at all.
Read this first: prefer a hook. An override is a copy that stops receiving updates. Every fix, accessibility change, and new feature in that template is now yours to merge by hand. If a hook can do the job, use the hook -- the full hook list is on this page.
How overriding works
Copy a template from the plugin into a wp-sell-services/ folder in your theme,
keeping the same filename and subfolder path. Your copy wins.
Lookup order:
- Child theme's
wp-sell-services/ - Parent theme's
wp-sell-services/ - The plugin's
templates/(fallback)
wp-content/plugins/wp-sell-services/templates/content-service-card.php
|
v
wp-content/themes/your-child-theme/wp-sell-services/content-service-card.php
Subfolders are preserved: templates/order/order-view.php becomes
your-theme/wp-sell-services/order/order-view.php.
Steps
- Create
wp-sell-services/in your child theme. - Copy the whole template across, keeping its path.
- Edit your copy.
- Clear every cache (object, page, CDN, browser) and test at desktop and 390px.
Overridable templates
Catalog and single pages
| Template | Controls |
|---|---|
archive-service.php |
The services catalog page |
archive-request.php |
The buyer requests board |
single-service.php |
A service detail page |
single-request.php |
A buyer request detail page |
content-service-card.php |
A service card in any grid |
content-request-card.php |
A request card in any list |
content-no-services.php |
Empty state for the catalog |
content-no-requests.php |
Empty state for the requests board |
wpss-fullwidth-template.php |
The full-width page wrapper |
Service page parts
| Template | Controls |
|---|---|
partials/service-gallery.php |
Image and video gallery |
partials/service-packages.php |
The Basic/Standard/Premium tabs |
partials/service-faqs.php |
FAQ accordion |
partials/service-reviews.php |
Review list and summary |
partials/vendor-card.php |
Vendor summary card |
partials/vendor-portfolio.php |
Portfolio grid |
partials/notifications-list.php |
Notification list markup |
partials/billing-fields.php |
Billing form fields |
partials/billing-summary.php |
Billing recap at checkout |
Vendor
| Template | Controls |
|---|---|
vendor/profile.php |
The public vendor profile |
Orders
| Template | Controls |
|---|---|
order/order-view.php |
The order detail screen |
order/order-confirmation.php |
Post-checkout confirmation |
order/conversation.php |
Order messaging thread |
order/order-requirements.php |
Requirements screen |
order/requirements-form.php |
The requirements form itself |
order/milestone-view.php |
A milestone phase |
order/extension-view.php |
A paid extension |
order/tip-view.php |
A tip receipt |
Dashboard
Each section of the unified dashboard is its own template under
dashboard/sections/:
orders.php · sales.php · services.php · create.php · requests.php ·
create-request.php · edit-request.php · favorites.php · earnings.php ·
messages.php · notifications.php · disputes.php · portfolio.php ·
profile.php
Other
| Template | Controls |
|---|---|
cart/cart.php |
Shopping cart |
myaccount/vendor-dashboard.php |
Legacy account-area vendor dashboard |
myaccount/vendor-services.php |
Legacy account-area service list |
myaccount/service-orders.php |
Legacy account-area order list |
myaccount/notifications.php |
Legacy account-area notifications |
The myaccount/ templates serve the standalone account area. Most sites use the
unified dashboard instead -- check which one your pages actually render before
overriding.
Emails
51 templates under emails/. They override the same way, but see
Email Customization first -- most
changes are better made through the email filters than by copying a template.
Template hooks
137 action hooks across 41 templates. Almost every template opens and closes
with a before/after pair and exposes named slots in between, so you can
inject markup without copying anything.
// Add a trust badge under every service card.
add_action( 'wpss_after_service_card', function ( $service_id ) {
if ( get_post_meta( $service_id, '_my_verified', true ) ) {
echo '<span class="my-badge">Verified</span>';
}
} );
Catalog
| Hook | Args |
|---|---|
wpss_before_service_archive / wpss_after_service_archive |
-- |
wpss_service_archive_header, wpss_service_archive_sidebar |
-- |
wpss_before_service_loop / wpss_after_service_loop |
-- |
wpss_before_request_archive / wpss_after_request_archive |
-- |
wpss_request_archive_header, wpss_request_archive_sidebar |
-- |
wpss_before_request_loop / wpss_after_request_loop |
-- |
wpss_no_services_content, wpss_no_requests_content |
-- |
Cards
| Hook | Args |
|---|---|
wpss_before_service_card / wpss_after_service_card |
$service_id |
wpss_service_card_header, wpss_service_card_meta, wpss_service_card_footer |
$service_id |
wpss_service_card_image_overlay |
$service_id |
wpss_before_request_card / wpss_after_request_card |
$request_id |
wpss_request_card_header, wpss_request_card_meta, wpss_request_card_footer |
$request_id |
wpss_before_vendor_card / wpss_after_vendor_card |
$vendor_id |
wpss_vendor_card_meta |
$vendor_id |
Single service
| Hook | Args |
|---|---|
wpss_before_single_service / wpss_after_single_service |
$service |
wpss_single_service_header, _gallery, _content, _faqs, _reviews, _sidebar, _portfolio, _related |
$service |
wpss_single_service_meta |
$service_id |
wpss_before_service_gallery / wpss_after_service_gallery |
$service_id |
wpss_before_service_packages / wpss_after_service_packages |
$service_id |
wpss_before_package_tab / wpss_after_package_tab |
$service_id, $index, $package |
wpss_package_features |
$service_id, $index, $package |
wpss_before_service_faqs / wpss_after_service_faqs |
$service_id |
wpss_before_service_reviews / wpss_after_service_reviews |
$service_id |
wpss_after_single_review |
$review |
Single request
| Hook | Args |
|---|---|
wpss_before_single_request / wpss_after_single_request |
$request_id |
wpss_single_request_header, _content, _proposals, _sidebar |
$request_id |
Vendor profile
| Hook | Args |
|---|---|
wpss_before_vendor_profile / wpss_after_vendor_profile |
$vendor_id |
wpss_vendor_profile_header, _bio, _services, _reviews, _stats, _sidebar |
$vendor_id |
wpss_before_vendor_portfolio / wpss_after_vendor_portfolio |
$vendor_id |
wpss_vendor_profile_sidebar is where Pro renders its analytics teaser -- a
useful precedent if you are adding your own panel.
Orders
| Hook | Args |
|---|---|
wpss_before_order_view / wpss_after_order_view |
$order |
wpss_order_view_header, _actions, _details, _sidebar |
$order |
wpss_before_order_confirmation / wpss_after_order_confirmation |
$order |
wpss_order_confirmation_details |
$order |
wpss_before_conversation / wpss_after_conversation |
$order |
wpss_conversation_header, wpss_conversation_form |
$order |
wpss_after_message |
$message, $order |
wpss_before_requirements_form / wpss_after_requirements_form |
$order |
wpss_requirements_form_fields |
$order |
wpss_before_requirements_form_component / wpss_after_requirements_form_component |
$order_id, $order |
wpss_before_milestone_view / wpss_after_milestone_view |
$current_order |
wpss_before_extension_view / wpss_after_extension_view |
$current_order |
wpss_before_tip_view / wpss_after_tip_view |
$current_order |
Dashboard
Every section fires the same pair, with the section slug as the first argument:
| Hook | Args |
|---|---|
wpss_dashboard_section_before |
$section, $user_id |
wpss_dashboard_section_after |
$section, $user_id |
Slugs: orders, sales, services, create, requests, create_request,
edit_request, favorites, earnings, messages, notifications,
disputes, portfolio, profile.
add_action( 'wpss_dashboard_section_before', function ( $section, $user_id ) {
if ( 'earnings' === $section ) {
echo '<div class="notice">Payouts run on Fridays.</div>';
}
}, 10, 2 );
Section-specific slots:
| Hook | Args |
|---|---|
wpss_earnings_summary, wpss_earnings_ledger_actions |
$user_id |
wpss_payout_methods |
$user_id, $payout_method |
wpss_orders_filters |
$user_id |
wpss_services_list_actions |
$user_id |
wpss_profile_form_fields |
$user_id |
Two sections pass a different second argument -- profile passes $user (a
WP_User) and portfolio passes get_userdata( $user_id ). If your callback
expects an id everywhere, guard for it.
Disputes
| Hook | Args |
|---|---|
wpss_before_dispute_view / wpss_after_dispute_view |
$dispute, $order |
wpss_dispute_view_header, _evidence, _resolution |
$dispute, $order |
Template filters
Change values without touching markup at all.
| Filter | Default | Purpose |
|---|---|---|
wpss_archive_service_columns |
3 |
Catalog grid columns |
wpss_archive_request_columns |
2 |
Requests grid columns |
wpss_services_per_page |
12 |
Catalog page size |
wpss_requests_per_page |
10 |
Requests page size |
wpss_reviews_per_page |
10 |
Reviews shown per service |
wpss_service_card_classes |
['wpss-service-card'] |
CSS classes on a card |
wpss_request_card_classes |
['wpss-request-card'] |
CSS classes on a request card |
wpss_service_card_thumbnail_size |
medium_large |
Card image size |
wpss_gallery_image_size |
large |
Gallery image size |
wpss_package_price_html |
-- | Rendered package price markup |
wpss_package_button_text |
-- | Package CTA label |
wpss_order_status_label |
-- | Human status label |
wpss_order_actions |
-- | Buttons on the order screen |
wpss_tip_quick_amounts |
[5, 10, 20, 50] |
Tip preset buttons |
wpss_allow_late_requirements_submission |
false |
Accept requirements after the timeout |
wpss_requirements_form_args |
-- | Requirements form config |
wpss_vendor_profile_fields |
[] |
Extra profile fields |
wpss_single_service_layout |
default |
Layout variant |
wpss_single_request_layout |
default |
Layout variant |
wpss_no_services_message |
-- | Empty-state copy |
wpss_no_requests_message |
-- | Empty-state copy |
wpss_get_template |
-- | Swap a resolved template path |
wpss_get_template_part |
-- | Swap a resolved template part |
wpss_template_args |
-- | Modify the args passed into a template |
wpss_get_template is the surgical option when you want a different file for
one case only, without a blanket override:
add_filter( 'wpss_get_template', function ( $template, $name ) {
if ( 'content-service-card.php' === $name && is_tax( 'wpss_service_category', 'premium' ) ) {
return get_stylesheet_directory() . '/wpss-premium-card.php';
}
return $template;
}, 10, 2 );
Guidance
- Always use a child theme. Overrides in a parent theme are lost when it updates.
- Copy the whole file. Partial templates fatal; the loader includes the file as-is.
- Re-check after plugin updates. If a default template changed, diff it against your copy and merge. Overrides are the main cause of "broken after update".
- Do not strip functionality. Keep nonces, form fields, and data attributes; restyle around them.
- Test at 390px, and in dark mode if your theme supports it.
Troubleshooting
| Problem | Cause |
|---|---|
| Override ignored | Path must be exactly your-theme/wp-sell-services/{same/path}.php, case-sensitive |
| Still ignored | Object or page cache; flush both |
| Fatal after copying | Partial copy, or a missing variable the template expects |
| Broken after an update | Default template changed structurally -- diff and merge |
| Theme styles clash | Add CSS in the child theme rather than editing the template |
Related
- Hooks and Filters -- the complete reference
- Theme Integration -- design tokens and dark mode
- Email Customization
- Shortcodes Reference