Developer reference
Template overrides, PHP filters and actions, JavaScript events, CSS tokens, REST API and importers.
Looking for something else?
Or ask a real person →Template overrides, PHP filters and actions, JavaScript events, CSS tokens, REST API and importers.
Looking for something else?
Or ask a real person →This is the Developers section of the help desk. Everything here is a public, supported extension point; anything not listed is internal and may change.
Copy any file from the plugin’s templates/ folder into your theme (or child theme) under scrumptious/, keeping the same path. Your copy wins.
| Plugin file | Theme override | What it renders |
|---|---|---|
templates/recipe-card.php | your-theme/scrumptious/recipe-card.php | The whole card wrapper and section order |
templates/parts/summary.php | your-theme/scrumptious/parts/summary.php | Summary text |
templates/parts/rating.php | your-theme/scrumptious/parts/rating.php | Star rating (display + voting) |
templates/parts/meta.php | your-theme/scrumptious/parts/meta.php | Times & servings |
templates/parts/ingredients.php | your-theme/scrumptious/parts/ingredients.php | Ingredient groups |
templates/parts/instructions.php | your-theme/scrumptious/parts/instructions.php | Instruction groups and steps |
templates/parts/notes.php | your-theme/scrumptious/parts/notes.php | Notes |
templates/parts/nutrition.php | your-theme/scrumptious/parts/nutrition.php | Nutrition facts |
templates/parts/share.php | your-theme/scrumptious/parts/share.php | Share buttons (keep .swp-share, data-url, .swp-share__copy and .swp-share__native for Copy link and the phone share menu) |
templates/parts/gallery.php | your-theme/scrumptious/parts/gallery.php | Photo gallery (links with class swp-gallery__link open in the lightbox) |
templates/parts/actions.php | your-theme/scrumptious/parts/actions.php | Print / Cook mode buttons (Recipe Part block) |
Parts are shared by the full Recipe Card, the Recipe Part blocks and the recipe page template, so one override changes the section everywhere.
Variables in recipe-card.php: $recipe (recipe data), $ctx (images, url, terms, rating), $recipe_id, $title, $class (extra classes), $show (section visibility), $style (full|compact|minimal), $print_url, $features (scale|units|cook|rate), $rating (['average','count'] or null).
Variables in parts: $recipe, $recipe_id, $rating, $print_url.
Keep the swp-* class names and data-* attributes if you want scaling, unit conversion, check-off and ratings to keep working — the script finds elements by them (.swp-card, .swp-ingredient[data-amount], .swp-ingredient__amount, .swp-ingredient__unit, .swp-step, .swp-rating).
To load templates from a plugin instead of a theme, use the scrumptious_template filter (below).
Tip: when the plugin updates a template, check your override against the new version. Template changes are listed in the changelog.
$recipe is the normalized recipe array:
[
'summary' => '', // HTML (kses)
'servings' => 4,
'servings_unit' => 'cookies',
'times' => [ 'prep' => 15, 'cook' => 10, 'rest' => 0, 'total' => 25 ], // minutes
'ingredient_groups' => [ [ 'title' => '', 'items' => [ [ 'raw','amount','amount_value','amount_max','unit','unit_key','name','notes','link' ] ] ] ],
'instruction_groups' => [ [ 'title' => '', 'steps' => [ [ 'text','image_id','timer' ] ] ] ],
'notes' => '',
'video_url' => '',
'keywords' => '',
'nutrition' => [ 'calories' => '', ... ],
'gallery' => [ 45, 46 ], // attachment IDs
'source_url' => '', // "Adapted from" link
'source_name' => '',
]
Read it anywhere with Scrumptious\Recipe::get( $recipe_id ). Stored in post meta _swp_recipe (JSON) on the swp_recipe post type; also exposed over the REST API as meta._swp_recipe.
Taxonomies: swp_course, swp_cuisine, swp_diet.
| Filter | Arguments | Use it to |
|---|---|---|
scrumptious_recipe_data | $recipe, $post | Change recipe data before display (card, parts, schema). Stored data is untouched. |
scrumptious_card_sections | $show, $recipe_id, $args | Show/hide image, summary, times, notes, nutrition per recipe. |
scrumptious_card_classes | $classes, $recipe_id, $args | Add CSS classes to the card wrapper. |
scrumptious_card_html | $html, $recipe_id, $recipe | Wrap or post-process the final card HTML. |
scrumptious_reader_features | $features, $recipe_id | Turn scale / units / cook / rate on or off per recipe. Empty array = no JavaScript. |
scrumptious_template | $path, $name | Load a template file from somewhere else. |
scrumptious_schema | $node, $recipe, $post | Edit the Recipe JSON-LD. |
scrumptious_output_schema | $output | Return false to print no JSON-LD at all (e.g. your SEO plugin handles it). |
scrumptious_recipe_url | $url, $recipe_id | Change a recipe’s primary URL (grid links, print “Recipe from”, schema url). |
scrumptious_grid_query_args | $args, $attributes | Change the Recipe Grid query. |
scrumptious_trusted_proxies | $ranges | CIDR ranges whose CF-Connecting-IP / X-Real-IP header is trusted for the visitor IP (Cloudflare’s ranges by default). Add your load balancer here. |
scrumptious_client_ip | $ip | Final say on the visitor IP (ratings, submission limits). |
scrumptious_ai_hourly_limit | $limit | AI requests per user per hour from the editor (default 60; admins unlimited). |
scrumptious_importers | $importers | Add an importer for another recipe plugin (extend Scrumptious\Importer: slug, label, ids, load, replace, markers). |
scrumptious_import_data | $data, $importer, $source_id | Adjust a recipe before it’s imported. |
scrumptious_card_credit | $name, $recipe, $recipe_id | The Recipe by … line (” hides it). |
scrumptious_share_position | $position, $recipe_id | 'bottom' or 'top' for one recipe’s share buttons. Share part templates get $share_position. |
scrumptious_share_icons | $icons | Replace share icons: network => 24×24 path data or full <svg> markup. |
scrumptious_share_links | $data, $recipe_id | Add share networks or change labels/URLs. $data = { url, title, links: { network: { label, href } }, native, copy }. |
scrumptious_help_url | $url, $slug | Point the plugin’s help links at your own docs (e.g. for white-label client sites). |
scrumptious_parse_prompt | $prompt, $existing | Change the AI instructions used by Paste & Parse. $existing = the site’s course/cuisine/diet terms (taxonomy => [ id => name ]). |
scrumptious_parse_result | $result, $text | Adjust a Paste & Parse result (title, recipe, source, notice, terms) before it reaches the editor. terms = taxonomy => term IDs (AI only). |
scrumptious_tag_prompt | $prompt, $existing | Change the AI instructions for course/cuisine/diet suggestions (also appended to the Paste & Parse prompt). Use it to add house rules, e.g. “Never suggest Dinner; use Main”. |
scrumptious_suggest_terms | $suggestions, $title, $recipe | Adjust Suggest with AI results. $suggestions = taxonomy => [ { id, name } ]; id 0 means a new term. |
| Action | Arguments | Fires |
|---|---|---|
scrumptious_before_card | $recipe_id, $recipe, $args | Before the card markup |
scrumptious_after_card | $recipe_id, $recipe, $args | After the card markup |
scrumptious_before_part | $part, $recipe_id, $recipe | Before any section |
scrumptious_after_part | $part, $recipe_id, $recipe | After any section |
scrumptious_before_part_{part} | $recipe_id, $recipe | Before one section, e.g. scrumptious_before_part_ingredients |
scrumptious_after_part_{part} | $recipe_id, $recipe | After one section, e.g. scrumptious_after_part_instructions |
scrumptious_card_actions | $recipe_id, $recipe | Inside the card’s button row, after Print (echo extra buttons) |
scrumptious_card_end | $recipe_id, $recipe | Inside the card, after the last section (credit lines) |
scrumptious_imported_recipe | $recipe_id, $source_id, $importer, $data | After a recipe was imported (copy extra fields here). |
Part names: summary, rating, meta, ingredients, instructions, notes, gallery, share, nutrition, actions.
Example — an affiliate “Shop the ingredients” box after the ingredients:
add_action( 'scrumptious_after_part_ingredients', function ( $recipe_id ) {
echo '<p class="my-shop-link"><a href="/shop/?recipe=' . (int) $recipe_id . '">Shop the ingredients</a></p>';
} );
Every card dispatches bubbling DOM events, so you can listen on document:
| Event | event.detail |
|---|---|
scrumptious:ready | { recipeId } |
scrumptious:change | { scale, system } — servings scaled or units switched |
scrumptious:cook | { on } — cook mode toggled |
scrumptious:rated | { rating, average, count } |
scrumptious:share | { network, url } — a share button was clicked (native, copy, pinterest, facebook, x, whatsapp, email) |
document.addEventListener( 'scrumptious:rated', ( e ) => {
window.gtag && gtag( 'event', 'recipe_rated', { value: e.detail.rating } );
} );
Editor filters (wp.hooks) let you add input types to Paste & Parse. Scrumptious Pro uses them for Import from a link.
| Filter | Value |
|---|---|
scrumptious.pasteParse.sources | Array of { test( text ), request( text, options ), defaults, Options, button, help }. The first source whose test() returns true handles the text; request() returns apiFetch options, and the response must match /scrumptious/v1/parse. Options is a component that gets { options, setOption }. |
scrumptious.pasteParse.intro / scrumptious.pasteParse.placeholder | The box’s intro text and placeholder. |
Cards use CSS custom properties; override them in your theme:
.swp-card, .swp-grid, .swp-jump {
--swp-accent: #2b8a3e; /* buttons, headings, step numbers, stars */
--swp-muted: #5c5f66;
--swp-border: #eee4e9;
--swp-bg: #fff;
--swp-radius: 14px;
}
The accent can also be set in Recipes → Settings. The default is Raspberry #e11d74 on a white card (4.5:1 contrast with white text). If you pick your own accent, check that white text on it stays readable. Style variants add .swp-card--compact / .swp-card--minimal; cook mode adds .is-cooking; checked-off items get .is-done.
scrumptious/recipe, scrumptious/jump, scrumptious/recipe-part (attribute part), scrumptious/recipe-grid. All are dynamic (server-rendered), so theme overrides apply to them.single-swp_recipe) and Recipe Index (archive-swp_recipe) templates, editable in the Site Editor. Classic themes can add single-swp_recipe.php / archive-swp_recipe.php as usual.[scrumptious id="123"] and [scrumptious-jump label="" print="1"].| Hook | Args | Use |
|---|---|---|
scrumptious_pro_loaded (action) | $tier | Fires when Pro has loaded, with '', pro, studio or agency. |
scrumptious_pro_tier | $tier | Override the tier (e.g. studio on a local development site). |
scrumptious_pro_features | $features | Which tier each Pro feature needs (feature => tier). |
scrumptious_pro_can | $can, $feature | Final say on whether a feature is available. |
| scrumptious_submission_data | $data, $fields | Change a submitted recipe before it’s saved. | | scrumptious_recipe_submitted (action) | $recipe_id, $fields | A submission was saved as a Pending recipe. | | scrumptious_submission_handled (action) | $recipe_id, $code, $fields | Every submit attempt, with $code ” or an error (spam, rate, missing…). | | scrumptious_submission_admin_email / scrumptious_submission_published_email | $email, $recipe_id | Change or cancel (to = ”) the emails. | | scrumptious_submit_form_context | $context | Template variables for the form. |
| scrumptious_bulk_ai_prompt / scrumptious_bulk_ai_changes | $prompt, $tasks / $changes, $recipe_id, $ai | Change the Bulk AI instructions, or adjust/skip changes before they’re saved. | | scrumptious_bulk_ai_processed (action) | $recipe_id, $changes | After Bulk AI changed a recipe. |
| scrumptious_meal_plan_button / scrumptious_meal_plan_url / scrumptious_meal_plan_browse_url | $show, $recipe_id / $url / $url | Meal planner: show the card button, its target page, the empty-plan browse link. | | scrumptious_import_url_max_image | $bytes | Largest photo Import from a link copies (default 10 MB). | | scrumptious_meal_plan_search_args | $args, $q | Meal planner: the WP_Query args behind a day’s + Add a recipe search. | | scrumptious_pro_support_email / scrumptious_pro_support_details / scrumptious_pro_support_mail | $email / $details / $mail | Priority support (Recipes → Support): where messages go (an agency can route them to itself), the site details attached, and the email before it’s sent. | | scrumptious_pro_support_sent (action) | $ok, $mail | After a support message was sent, or failed. | | scrumptious_shopping_list / scrumptious_shopping_aisles / scrumptious_shopping_list_url | $list, $recipes / $rules / $url | Shopping list: change the finished list, the aisle rules, the page the meal plan links to. | | scrumptious_url_import_result | $result, $url, $node | Adjust an Import from a link result (Pro) ($node is the schema.org Recipe found on the page, or null). | | scrumptious_import_url_ai_terms | $fill (bool, default true) | Return false to stop Import from a link (Pro) asking AI for a course, cuisine or diet the page left out. |
REST (Pro): GET /scrumptious-pro/v1/plan-recipes?ids=1,2 (anyone), GET /scrumptious-pro/v1/plan-search?q= (anyone; newest when empty), GET\|POST /scrumptious-pro/v1/meal-plan (logged-in users), POST /scrumptious-pro/v1/shopping-list { items: [ { id, servings } ] } (anyone; published recipes only), POST /scrumptious-pro/v1/import-url { url, photo, credit } (users who can edit posts; returns { title, recipe, source: 'schema'|'ai'|'basic', notice, terms, image_id }).
Template: {theme}/scrumptious/submit-recipe.php. DOM event: scrumptious:submitted.
PHP: Scrumptious_Pro\Plan::tier() and Scrumptious_Pro\Plan::can( 'submit_recipe' ).
| Route | Who | Does |
|---|---|---|
POST /wp-json/scrumptious/v1/parse { text, mode: auto|ai|basic } | Users who can edit posts | Paste & Parse. Returns { title, recipe, source: 'ai'|'basic', notice }. |
POST /wp-json/scrumptious/v1/suggest-terms { recipe_id, title?, recipe? } | Users who can edit that recipe | AI course/cuisine/diet suggestions. Send title/recipe to use unsaved editor data. Returns { swp_course: [ { id, name } ], swp_cuisine: […], swp_diet: […] }. |
GET /wp-json/scrumptious/v1/import | Admins | Import sources found on the site, with counts. |
POST /wp-json/scrumptious/v1/import/{source}/recipes|posts|undo { cursor } | Admins | Run one batch of an import step; repeat with the returned cursor until done. |
POST /wp-json/scrumptious/v1/rate { recipe_id, rating } | Anyone (rate-limited) | Star rating. |
PHP: Scrumptious\Recipe_Parser::parse( $text ) (no AI), Scrumptious\Parse_Api::parse( $text, 'auto' ) (AI when set up), Scrumptious\Tagger::suggest( $title, $recipe ) (AI course/cuisine/diet) and Scrumptious\Tagger::ids( [ 'swp_course' => [ 'Main' ] ] ) (names to term IDs, matching existing terms loosely and creating missing ones).
/recipe/slug/) is the primary page. It is self-canonical and is the only page that outputs the Recipe JSON-LD. Blog posts can still show the card, but don’t repeat the recipe data, so Google sees one recipe at one URL.