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 →

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.

1. Template overrides

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 fileTheme overrideWhat it renders
templates/recipe-card.phpyour-theme/scrumptious/recipe-card.phpThe whole card wrapper and section order
templates/parts/summary.phpyour-theme/scrumptious/parts/summary.phpSummary text
templates/parts/rating.phpyour-theme/scrumptious/parts/rating.phpStar rating (display + voting)
templates/parts/meta.phpyour-theme/scrumptious/parts/meta.phpTimes & servings
templates/parts/ingredients.phpyour-theme/scrumptious/parts/ingredients.phpIngredient groups
templates/parts/instructions.phpyour-theme/scrumptious/parts/instructions.phpInstruction groups and steps
templates/parts/notes.phpyour-theme/scrumptious/parts/notes.phpNotes
templates/parts/nutrition.phpyour-theme/scrumptious/parts/nutrition.phpNutrition facts
templates/parts/share.phpyour-theme/scrumptious/parts/share.phpShare buttons (keep .swp-share, data-url, .swp-share__copy and .swp-share__native for Copy link and the phone share menu)
templates/parts/gallery.phpyour-theme/scrumptious/parts/gallery.phpPhoto gallery (links with class swp-gallery__link open in the lightbox)
templates/parts/actions.phpyour-theme/scrumptious/parts/actions.phpPrint / 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.

2. Recipe data

$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.

3. PHP filters

FilterArgumentsUse it to
scrumptious_recipe_data$recipe, $postChange recipe data before display (card, parts, schema). Stored data is untouched.
scrumptious_card_sections$show, $recipe_id, $argsShow/hide image, summary, times, notes, nutrition per recipe.
scrumptious_card_classes$classes, $recipe_id, $argsAdd CSS classes to the card wrapper.
scrumptious_card_html$html, $recipe_id, $recipeWrap or post-process the final card HTML.
scrumptious_reader_features$features, $recipe_idTurn scale / units / cook / rate on or off per recipe. Empty array = no JavaScript.
scrumptious_template$path, $nameLoad a template file from somewhere else.
scrumptious_schema$node, $recipe, $postEdit the Recipe JSON-LD.
scrumptious_output_schema$outputReturn false to print no JSON-LD at all (e.g. your SEO plugin handles it).
scrumptious_recipe_url$url, $recipe_idChange a recipe’s primary URL (grid links, print “Recipe from”, schema url).
scrumptious_grid_query_args$args, $attributesChange the Recipe Grid query.
scrumptious_trusted_proxies$rangesCIDR 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$ipFinal say on the visitor IP (ratings, submission limits).
scrumptious_ai_hourly_limit$limitAI requests per user per hour from the editor (default 60; admins unlimited).
scrumptious_importers$importersAdd an importer for another recipe plugin (extend Scrumptious\Importer: slug, label, ids, load, replace, markers).
scrumptious_import_data$data, $importer, $source_idAdjust a recipe before it’s imported.
scrumptious_card_credit$name, $recipe, $recipe_idThe 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$iconsReplace share icons: network => 24×24 path data or full <svg> markup.
scrumptious_share_links$data, $recipe_idAdd share networks or change labels/URLs. $data = { url, title, links: { network: { label, href } }, native, copy }.
scrumptious_help_url$url, $slugPoint the plugin’s help links at your own docs (e.g. for white-label client sites).
scrumptious_parse_prompt$prompt, $existingChange the AI instructions used by Paste & Parse. $existing = the site’s course/cuisine/diet terms (taxonomy => [ id => name ]).
scrumptious_parse_result$result, $textAdjust a Paste & Parse result (title, recipe, source, notice, terms) before it reaches the editor. terms = taxonomy => term IDs (AI only).
scrumptious_tag_prompt$prompt, $existingChange 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, $recipeAdjust Suggest with AI results. $suggestions = taxonomy => [ { id, name } ]; id 0 means a new term.

4. PHP actions

ActionArgumentsFires
scrumptious_before_card$recipe_id, $recipe, $argsBefore the card markup
scrumptious_after_card$recipe_id, $recipe, $argsAfter the card markup
scrumptious_before_part$part, $recipe_id, $recipeBefore any section
scrumptious_after_part$part, $recipe_id, $recipeAfter any section
scrumptious_before_part_{part}$recipe_id, $recipeBefore one section, e.g. scrumptious_before_part_ingredients
scrumptious_after_part_{part}$recipe_id, $recipeAfter one section, e.g. scrumptious_after_part_instructions
scrumptious_card_actions$recipe_id, $recipeInside the card’s button row, after Print (echo extra buttons)
scrumptious_card_end$recipe_id, $recipeInside the card, after the last section (credit lines)
scrumptious_imported_recipe$recipe_id, $source_id, $importer, $dataAfter 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>';
} );

5. JavaScript events

Every card dispatches bubbling DOM events, so you can listen on document:

Eventevent.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.

FilterValue
scrumptious.pasteParse.sourcesArray 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.placeholderThe box’s intro text and placeholder.

6. Styling

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.

7. Blocks and templates

  • Blocks: scrumptious/recipe, scrumptious/jump, scrumptious/recipe-part (attribute part), scrumptious/recipe-grid. All are dynamic (server-rendered), so theme overrides apply to them.
  • With public recipe pages on, block themes get Single Recipe (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.
  • Shortcode fallbacks: [scrumptious id="123"] and [scrumptious-jump label="" print="1"].

Scrumptious Pro (add-on)

HookArgsUse
scrumptious_pro_loaded (action)$tierFires when Pro has loaded, with '', pro, studio or agency.
scrumptious_pro_tier$tierOverride the tier (e.g. studio on a local development site).
scrumptious_pro_features$featuresWhich tier each Pro feature needs (feature => tier).
scrumptious_pro_can$can, $featureFinal 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' ).

8. REST API

RouteWhoDoes
POST /wp-json/scrumptious/v1/parse { text, mode: auto|ai|basic }Users who can edit postsPaste & Parse. Returns { title, recipe, source: 'ai'|'basic', notice }.
POST /wp-json/scrumptious/v1/suggest-terms { recipe_id, title?, recipe? }Users who can edit that recipeAI 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/importAdminsImport sources found on the site, with counts.
POST /wp-json/scrumptious/v1/import/{source}/recipes|posts|undo { cursor }AdminsRun 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).

9. SEO model

  • Public recipe pages off: the recipe lives in the post that shows it. That post gets the Recipe JSON-LD and is the recipe’s URL.
  • Public recipe pages on: the recipe page (/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.