Skip to content
wp-skills
Register

Changelog

Every release of WP Skills, newest first.

  1. 3.9.0

    Latest

    The block nobody could see

    • AddedBlock Generator writes a whole working block: a block.json with the attributes and supports you picked, a render.php that runs on every request, the editor script that puts the block in the inserter, and the plugin file that ties the three together. No build step and no npm — the files that come out are the files WordPress loads, so you can drop the folder into wp-content/plugins and see the block in the editor.
    • AddedBlock Pattern Generator writes a pattern either way round. register_block_pattern() if it belongs to a plugin, or a file for your theme's patterns folder if it belongs to a theme — the same fields, rearranged into whichever of the two forms WordPress reads. The theme form also tells you why a brand new pattern file does not show up, which is a cache rather than your file.
    • AddedBlock Style Generator adds an entry to any block's Styles panel with register_block_style(). It tells you the class your CSS has to match — is-style- plus the name, never the label — and warns you that nothing anywhere checks the block name you typed: aim a style at a block that does not exist and WordPress accepts it in silence.
    • ChangedThe generators page has a Blocks section now, sitting right after Content. 44 generators across ten categories.
    • FixedA block registered only in PHP is described to the editor and never registered in it — it does not appear in the inserter, and existing posts show it as a missing block. That is why the block generator ships JavaScript at all, and it is not a guess: WordPress hands the editor every server-registered block, but that call stores the description and stops one step short of registering anything. The generated file says so, next to the line that does the registering.
    • FixedPointing block.json's editorScript at a file — the form every tutorial shows — registers that script with no dependencies at all, because the dependency list normally comes out of a build step nobody here ran. The script then loads before wp.blocks exists and the block quietly fails to register. The generated plugin registers the handle in PHP with its dependencies spelled out and names the handle in block.json instead.
    • ChangedEvery claim above was checked by running this generator's own output inside a real WordPress, not by reading the handbook. That is also where the apiVersion default turned out to be 1 rather than the current API, and where passing arguments alongside block.json turned out to override it — including the block's name, which produces a second block under a name you never chose.
  2. 3.8.0

    The values WordPress never knew about

    • AddedPost Meta Generator writes register_post_meta() for you: a declared type, an optional default, a sanitizer that runs for every writer rather than only the form you built, and a rule for who is allowed to change the value. Term meta and user contact methods were already here — this is the one most people need first.
    • AddedREST Route Generator builds a custom endpoint. You get register_rest_route() on the right hook, an argument schema WordPress validates and casts on its own, and a permission_callback you have to pick rather than leave blank. Choose the public option and the snippet tells you plainly that __return_true is the check, not a placeholder for one you still have to write.
    • AddedREST Field Generator adds a field to a response WordPress already serves. The two callbacks receive different things — the getter gets the prepared response as an array, the setter gets the real WP_Post — so the code names each parameter for what actually arrives. Reaching for $post->ID in the getter is how this usually breaks, and it breaks when someone calls the endpoint rather than when you write it.
    • FixedMeta Box Generator saved your values but never told WordPress they existed. That meant no declared type, no sanitizing for anything writing the value outside your own form, and nothing at all in the REST API or the block editor — a box that worked perfectly on the post screen and was invisible everywhere else. It now registers the keys as well. Snippets you saved before this release are untouched and keep producing the exact code you saved; the option is on for new boxes.
    • ChangedThe generators page has a REST API section now, and post meta sits with meta box and term meta under Admin. 41 generators across nine categories.
    • ChangedEverything above was checked by running the generated PHP inside a real WordPress before it shipped, which is how three of its claims got corrected rather than published. One example you would have hit on the first save: pointing an integer field's sanitizer at intval looks obviously right, parses cleanly, and is a fatal error, because WordPress hands that callback three arguments and PHP refuses to pass extras to its own built-in functions.
  3. 3.7.12

    Thirty-eight of something

    • ChangedThe generators index was one flat wall of thirty-eight cells, which asked a first-time visitor to choose without telling them anything about the choice. It is now grouped into eight sections — content, admin, plugins, queries, users, themes, assets and Elementor — and the search filters across all of them, matching a section's name as well as a tool's, so "queries" finds five generators that do not carry the word in their titles.
    • ChangedThe landing page led with the same thirty-eight cells. It now shows seven popular ones and links to the index for the rest.
    • ChangedEvery tool was titled some arrangement of "WordPress ... Generator", except the fourteen that were not, and two that had lost the word Generator entirely. On a site where every tool is a WordPress generator, neither word distinguished anything: the index now lists Post Type, Enqueue Scripts, Settings Page and wp-config.php. Only the labels changed — every URL, page title and saved snippet is untouched.
    • FixedThree pages announced "0 planned" next to the generator count, which reads as a project that has stopped rather than a catalogue that is finished. They now say only what exists, and the planned half returns on its own the day something is added to the roadmap.
    • FixedThe command palette printed a number beside each result that was supposed to be the one the index gave it, but computed it from its own private copy of the index's sort order. Grouping the index broke the agreement without breaking anything visibly: the palette called the widget generator 16 while the index called it 02. Both now read the same list.
  4. 3.7.11

    • FixedThe generators index spelled one tool "Wordpress Taxonomy Generator". Five comments still counted 21 or 36 generators, from back when there were.
  5. 3.7.10

    • FixedA page description longer than 160 characters was cut wherever character 160 happened to fall, usually through the middle of a word, and the ellipsis appended after the cut pushed the result three characters past the limit it was enforcing. It now ends on a word boundary, inside the limit, with no stranded punctuation before it. The page's own lead is untouched — only the tag a search result shows is shortened.
  6. 3.7.9

    The missing p

    • FixedThe custom user profile fields generator had shipped under a misspelt URL — wordress, without the p — since 2023. The path is spelt correctly now, and the old one redirects permanently rather than 404ing, saved-snippet links included.
  7. 3.7.8

    The ones that looked too small to test

    • AddedThe theme header, plugin header, child theme and sidebar generators had no behavioural test of any kind, and the admin notice, plugin header and login form generators had no adversarial case. Each is a header block or a single registration call, which reads as nothing to get wrong — and a snapshot freezes whatever comes out, right or wrong, while php -l is perfectly happy with a style.css carrying no headers at all.
  8. 3.7.7

    Twenty-one forms with nothing to say

    • ChangedThe generators built in 2023 shipped with bare labels and not one line of field help between them, while everything built since carries it on roughly half its fields. All of them now explain what a field does and what goes wrong when it is wrong: that a post status key is stored in a varchar(20) the database silently truncates, that a child theme's Template is the parent's folder name rather than its display name, that a blank login form label is a value rather than "leave it alone", and that a sidebar's class only reaches the page through its wrapper.
  9. 3.7.6

    A value that went nowhere

    • Fixedregister_sidebar() now emits before_sidebar and after_sidebar. Without a wrapper the sidebar's class argument was a value that went nowhere: dynamic_sidebar() runs sprintf( $before_sidebar, $id, $class ) and that is the only place a sidebar's class is ever used — before_widget's %2$s is the widget's classes, not the area's. Both are omitted when blank, because sprintf( '', … ) is still ''.
    • Addedshow_in_rest, which arrived in WordPress 5.6 and is what lets the block editor's widget screen manage the area. It is written out either way, because false is a claim worth seeing rather than an absence.
  10. 3.7.5

    A list you search, not one you scroll

    • AddedBoth hook pickers are searchable. 1,815 filters in a plain dropdown is a list you scroll rather than one you read, and adding the missing thousand without a search box would have made the control worse rather than better. The popup mounts the first hundred matches and narrows as you type.
    • AddedThe Hook Generator's fields explain themselves: that a filter has to return a value and the generated callback returns the one being filtered, and that WordPress hands a callback a single argument unless "Accepted args" says otherwise — declaring parameters is not enough on its own.
  11. 3.7.4

    Everything core actually fires

    • ChangedThe hook lists are extracted from a WordPress 7.1 checkout rather than maintained by hand: 697 actions and 1,815 filters, up from 509 and 931. render_block, pre_render_block, post_class, register_block_type_args, wp_theme_json_data_theme, interactivity_process_directives and around a thousand others were simply absent — the lists had not been touched since September 2023.
    • ChangedEach signature now comes from the docblock above the call rather than from the call itself, so the parameters are named the way the handbook names them: deleted_user offers $id, $reassign, $user instead of $id, null, $user, and customize_register offers $manager instead of $this. Hooks whose name is assembled at runtime — "save_post_{$post->post_type}" — are absent and have to be; which names they produce depends on the site.
  12. 3.7.3

    An argument is not a parameter

    • Fixed61 of the hooks the Hook Generator offered produced PHP that does not parse. Its list was scraped from core's do_action() and apply_filters() call sites, and an argument at a call site is an expression rather than a parameter name: 35 hooks carried $this, which has been a fatal error as a parameter since PHP 7.1, and others carried null, false, B_IN_BYTES, (array) $taxonomy_object and $where . $groupby . $orderby. Three were scraper debris outright — a hook named widgets whose whole signature was p'. Every signature is now reduced to names PHP accepts, keeping the position and the variable inside an expression where there is one.
    • AddedThe whole option list goes through php -l on every test run — 1,440 hooks, because with this generator the list is the entire surface and nothing was checking it. A process per hook costs 93 seconds, so the callbacks are namespaced and compiled together, and a failing batch is bisected until each offender can be named on its own. Green costs one process and about a second.
  13. 3.7.2

    A comment has no quotes in it

    • FixedThree generators printed user text into a PHP comment without escaping it. A shortcode attribute default containing a newline ended the `// Shortcode: [...]` example line and dropped the rest of it onto a line of its own as top-level code. A Google Font family name containing */ closed the generated file's docblock six lines early, taking the `if ( ! defined( 'ABSPATH' ) )` guard with it. And the image size generator's crop position reached a PHP literal with no escaping at all — the only stored value in any generator that did.
    • AddedEvery field of every generator is now filled with a newline, and again with */ '\" \\ $x <?php, and the result run through the real parser. The adversarial string the suites already had is built from the characters that close a quoted literal, and a comment has no quotes in it — which is why all three faults above passed every existing test. The sweep covers all 38 generators and every file each of them emits.
  14. 3.7.1

    The wrong breakpoint

    • FixedEight forms still laid their repeatable rows out with md:grid-cols-2, and the meta box generator's post type list with md:grid-cols-2 xl:grid-cols-3. Those are viewport breakpoints, and the fields pane stops growing at about 522px however wide the window gets — so xl: fired, and split the column three ways, at exactly the point the column was at its narrowest share of the screen. They now use the same auto-fit grid as every generator built since, which consults no viewport and chooses no column count.
  15. 3.7.0

    A plugin is a folder

    • AddedThe Starter Plugin Generator builds a whole plugin folder: the main file with its header and constants, an optional main class in includes/, activation and deactivation hooks, and an uninstall routine. Every part is placed in the file WordPress actually reads it from — measured, not assumed. WordPress scans only the top level of a plugin folder for a Plugin Name header, so a header in includes/ makes the plugin vanish from the Plugins screen entirely; and a folder with two headered top-level files becomes two plugins, which is why the generated uninstall.php carries no header of its own.
    • AddedThe activation hook stays in the main file, because that is the only place it works. register_activation_hook( __FILE__ ) called from an include registers the callback under that include's path, which WordPress never activates — no error, no warning, the hook simply never runs. Verified on WordPress 7.1, alongside register_uninstall_hook(), which serialises its callback into an option and therefore fatals on activation if you hand it a closure: "Serialization of 'Closure' is not allowed".
    • AddedIt also gets flush_rewrite_rules() right, which most advice does not. Inside an activation hook did_action( 'init' ) is already 1, and yet the plugin's own post type does not exist: the file was included after init had fired, so its callback never ran. Flushing there rebuilds the rules without the post type in them. The generated activation hook calls the registration function first and then flushes — measured, and the difference between 23 matching rewrite rules and none.
    • Addedload_plugin_textdomain() is off by default, because it is usually unnecessary. Translations delivered by WordPress.org load on first use with no call at all — measured on 7.1 with nothing but a .mo file in place. When it is switched on, for a plugin shipping its own /languages, it hooks on init rather than plugins_loaded: WordPress 6.7's "triggered too early" notice actually checks against after_setup_theme, one hook earlier than its own message names.
    • AddedThe Elementor Widget Generator builds the widget class and the lines that register it. Content controls the template reads, style controls Elementor turns into CSS, per-device values through add_responsive_control, and a typography group control that expands into ten. It registers on elementor/widgets/register through $widgets_manager->register() — the older widgets_registered hook and register_widget_type() both still work in Elementor 4.2.4, and both have been deprecated since 3.5.0.
    • AddedIt leaves the Advanced tab alone, because Elementor supplies one: a widget declaring no controls at all already carries 166 of them. It also always writes get_title(), since a widget that omits it registers happily and appears in the panel with no name — the only method \Elementor\Widget_Base genuinely requires is get_name(). A category of your own comes with its own registration, because a widget naming an unregistered category is not an error, it is simply invisible.
    • ChangedThe output panel now shows several files when a generator produces several, as tabs. Only the open one is rendered, so the panel highlights exactly one file whether a generator emits one or four. Saving a snippet is unchanged: the files are derived from the form's values, and the values are still the only thing stored.
    • FixedFifteen generators were telling you to save their output as snippet.php. Two produce a style.css, one a theme.json, one a wp-config.php, and eleven more produce a whole PHP file of their own — a file that opens its own <?php tag and therefore cannot be pasted into functions.php at all. Each now shows the name it should be saved under, and class files follow WordPress's own class-{name}.php convention.
    • FixedThe syntax gate was reading nothing for the two generators that emit CSS. php -l accepts any file containing no <?php tag, because everything outside one is inline HTML as far as the parser is concerned — so a style.css with a broken header block passed. Those now go through a real CSS parser, which catches exactly the failure that matters: a header value containing */ ends the comment early and takes the rest of the theme's headers with it.
  16. 3.6.0

    Two whole files

    • AddedThe wp-config.php Generator builds the whole file rather than a snippet: database credentials, eight freshly generated secret keys, debugging, file permissions, revisions, cron and update policy. Every constant is placed above the wp-settings.php require, which is the only place a constant does anything — one defined after that line has already been given WordPress's default, so the second define() raises "Constant X already defined" and changes nothing. Verified by booting the generated file in WordPress 7.1 and watching WP_CACHE fail to load advanced-cache.php.
    • AddedIt also draws the line between the two hardening constants people reach for interchangeably. DISALLOW_FILE_EDIT removes the plugin and theme file editors and nothing else — installing and updating still work. DISALLOW_FILE_MODS denies those too, twelve capabilities including update_plugins, so a site that sets it stops receiving plugin security updates from the admin. Both were measured against an administrator rather than quoted.
    • AddedThe keys come from crypto.getRandomValues(), not Math.random(), and there are eight of them rather than seven — the AUTH_KEY family has a _SALT counterpart for each of its four _KEYs. WP_DEBUG_LOG is offered as a path as well as a switch, because wp_debug_mode() uses any string that is not "true" or "1" as the log file verbatim.
    • AddedThe theme.json Generator covers a block theme's colour, typography and spacing presets, layout widths, the styles applied to the page and to links, buttons and headings, and per-block overrides. Preset slugs are written in the form WordPress will actually use: it kebab-cases every slug and splits letters from digits, so brand2 becomes --wp--preset--color--brand-2 and a file that says brand2 sends you hunting for a variable that does not exist.
    • AddedIt also refuses to let you write something that quietly does nothing. A theme preset whose slug collides with one of WordPress's own is discarded outright — a palette entry slugged black stays #000000 — unless the matching default palette, font size or spacing switch is turned off, and the form says so. appearanceTools turns on eighteen settings by itself, so while it is on the nine switches it covers are disabled and left out of the file rather than repeated.
    • ChangedThe output panel now shows the name of the file it is producing. It had been able to since it was written and never was told: wp-config.php and theme.json show their own names, and everything else still reads snippet.php. The name is also what picks the syntax highlighting, so theme.json is highlighted as JSON — the two grammars load separately, so a PHP page never downloads the JSON one.
  17. 3.5.0

    Widgets, theme support and web fonts

    • AddedThe Widget Generator builds a WP_Widget class with its form, its save routine and its front end. Classic widgets are not dead: one registered this way still appears in the block widget editor as a Legacy Widget, alongside every one of WordPress's own. Every input's name comes from get_field_name() rather than being typed out, because a hand-written one posts under a name the save routine never looks at and nothing errors.
    • AddedIt also writes the note nobody expects to need. A widget rendered by a sidebar and a widget rendered by the_widget() do not get the same arguments — the first fills in two placeholders, the second fills in one — so handing the sidebar's markup to the_widget() is a fatal error on PHP 8. The snippet says so, with the working version written out.
    • AddedThe Add Theme Support Generator declares what your theme supports in one after_setup_theme callback. Featured images are emitted as exactly one call, because once they are registered for everything a later call limiting them to certain post types is silently ignored. HTML5 is emitted once too — a second call merges into the first without removing the duplicates.
    • AddedTwo of those features are refused outright without a list, and the generator says which. add_theme_support( 'html5' ) with no types gets you three WordPress picked for you; add_theme_support( 'post-formats' ) with no list gets you nothing at all. Of the seven HTML5 types the handbook documents, WordPress reads five — style and script are declared by WordPress itself and read by nobody.
    • AddedThe Google Fonts Loader Generator builds a css2 URL from the families, weights and italics you pick and enqueues it on the hooks you choose. The version argument is null and spelled out, because leaving it off makes WordPress append ?ver= its own version number to Google's URL and change it on every update. The preconnect goes to fonts.gstatic.com, where the font files actually come from — WordPress already handles the other host by itself.
  18. 3.4.0

    Settings pages and the toolbar

    • AddedThe Settings / Options Page Generator builds a real Settings API page: a menu entry under Settings, Tools, Appearance, Users, Media, the Dashboard or a top-level menu of its own, as many fields as you need, and one option row holding all of them. The option group is named once and used by both register_setting() and settings_fields(), because a mismatch between those two is invisible until you press Save — the page renders, the form submits, and WordPress stops on "the options page is not in the allowed options list" with nothing written.
    • AddedThe generated sanitize callback is built around three things WordPress actually does rather than what it looks like it does. It runs twice on the first save, so it is idempotent. Returning null from it does not leave the option alone — it empties the whole row — so it never does. And wp-admin/options.php hands it null itself when the form posts nothing under the option name, which is exactly what a page of unchecked checkboxes posts, so it treats that as "every box off" instead of "delete everything".
    • AddedThe Admin Bar Generator adds nodes to the toolbar and removes the ones you do not want. Parents come from a list of node ids WordPress really creates, because a node hung off an id that does not exist is dropped when the bar renders with nothing in the markup and nothing in the log to say why. Only the ten meta keys WP_Admin_Bar reads are offered — an invented one is a line in your snippet that does nothing.
    • AddedRemoving a core node is put on wp_before_admin_bar_render rather than on admin_bar_menu. remove_node() can only remove what already exists, and WordPress adds Comments at priority 60 and + New at 70 — so the obvious version, a removal at the default priority 10, runs first and quietly does nothing at all.
  19. 3.3.0

    Taxonomies, term fields and profile fields

    • AddedThe Taxonomy Generator builds a register_taxonomy() call: hierarchical like categories or flat like tags, the post types it attaches to, permalinks, REST, a default term and its own capabilities. It writes the label set WordPress actually uses for the hierarchy you picked — a hierarchical taxonomy has no "Separate items with commas" and a flat one has no parent, and emitting the wrong half leaves lines in your snippet that never appear anywhere.
    • AddedThe Term Meta Generator adds custom fields to a taxonomy's term screens, with a save routine that sanitizes per field type and a column in the terms list. The Add Term form and the Edit Term form take different markup — one is a stack of blocks, the other is rows inside a table — so it writes both rather than the one that looks right on whichever screen you opened first.
    • AddedThe User Contact Methods Generator adds fields to the Contact Info section of the profile screen. It emits no save routine, because WordPress already saves and sanitizes these itself — the snippet is the filter and nothing else, with the option to register the meta for the REST API where it genuinely is needed.
    • FixedThree options on the Custom Post Type Generator did nothing. Page attributes, Post formats and Custom fields could be switched on but never reached the generated code, so a post type built for ordering pages came out without the box that orders them. The other eight supports were unaffected, and snippets you had already saved are unchanged.
  20. 3.2.0

    Five ways to query WordPress

    • AddedThe Query Loop Generator builds a secondary WP_Query and the loop that goes with it — filters, ordering, meta and pagination that actually pages. It always emits the wp_reset_postdata() call that a secondary loop needs: without it every template tag after the loop reads the last post the loop touched, so the page below it is quietly wrong and nothing errors.
    • AddedThe Tax Query Generator filters that query by taxonomy terms, with as many clauses as you need and AND or OR between them. Term IDs are emitted as numbers, the EXISTS operators drop the terms WordPress would ignore, and a clause left with nothing usable to match says so rather than silently returning an empty list.
    • AddedThe Term Query Generator builds a WP_Term_Query for any taxonomy. The loop it writes matches what you asked the query to return: ask for slugs and you get a loop over strings, ask for IDs and it resolves them first, ask for a count and there is no loop at all.
    • AddedThe User Query Generator builds a WP_User_Query, with the wildcards that turn its search box into a substring match, the distinction between holding every listed role and holding any of them, and the option to skip the total row count nothing on the page is reading.
    • AddedThe Comment Query Generator builds a WP_Comment_Query using the status words the database recognises rather than the ones the admin screens show — a query for "approved" comments matches nothing and looks like a site with no comments.
    • ChangedArguments that WordPress emits as a list are now edited as a list. Theme tags, role capabilities and post type supports were rows of individual switches, several screens tall between them; they are now compact multi-selects that show at a glance which values are on.
    • ChangedGenerator forms now lay their fields out according to the width of the form column rather than the width of the browser window. The two had drifted apart: the column stops growing while the window keeps going, so the widest screens were the ones that packed fields tightest.
    • FixedSwitch labels no longer break one word per line. On the Custom Post Type, Post Status, Login Form and Cron Job generators, four fields were being squeezed into a column with room for two, and a number input on the Cron Job Generator had shrunk to a few characters wide.
  21. 3.1.0

    Every generator, checked against WordPress

    • FixedThe Meta Box Generator emitted PHP that would not compile as soon as a box held two fields whose types shared a branch — a text field and an email field, the most ordinary pairing there is. The generated switch carried two default clauses, which PHP rejects outright, so pasting the snippet took the site down instead of merely misbehaving.
    • FixedMeta boxes saved nothing. The security token was written using the title exactly as you typed it and read back in lower case, so any capital letter — including the one in the title the form starts with — meant the check looked for something that was never sent. The box appeared, accepted input, and discarded it on every save.
    • FixedSeveral Meta Box Generator field types were wrong in their own right: a media field stored an attachment ID even when you asked for a URL and never rendered its preview, a WYSIWYG field came out as a plain text input rather than an editor, a preselected radio or select option was dropped, and a checkbox you unticked re-ticked itself on the next page load.
    • FixedEnqueue Styles registered against four hooks that do not exist in WordPress. Nothing errored, because add_action accepts any name — the callback simply never ran and no stylesheet was ever loaded. Theme-relative paths were also a parse error, closing a bracket they never opened.
    • FixedThe Mail Function Generator was missing a semicolon, and built a headers array it then forgot to pass to wp_mail. Every message went out as plain text from the default address, with the sender, Cc and Bcc silently dropped — and Reply-To was never written at all.
    • FixedThe Hook Generator ignored the hook you picked. Every snippet came out attached to the same obscure Customizer hook, always through add_action even when you had chosen a filter, and the filter body returned something PHP cannot parse.
    • FixedThe Custom User Role Generator produced a role with no capabilities, because it listed them where WordPress expects them as keys, and then never registered the role at all — the function it wrote was defined and never called.
    • FixedThe Elementor Plugin Generator used two hooks that Elementor removed in 3.5, so a generated addon loaded cleanly and never registered a single widget.
    • FixedThe Custom Post Type Generator ignored its own permalink checkboxes — including the URL-base setting most people open it for — turned the query variable off whenever the field was left blank, and started you off with every option disabled, which registers a post type you cannot see in the admin, cannot query, and whose preview answers 404.
    • FixedThe Cron Job Generator scheduled events from a local-time value where WordPress requires UTC, shifting the first run by your timezone offset, and re-scheduled the same event on every single page load whenever the job took arguments.
    • FixedThe Menu Generator's untouched output declared a function with the same name as the WordPress function it calls, so copying it verbatim produced a fatal error.
    • FixedAn apostrophe anywhere in any generator broke the snippet. Nothing escaped the text you typed, so a title like O'Brien's Notes ended the PHP string early and the file stopped parsing — a fatal error from an ordinary name.
    • FixedCustom User Profile Fields wrote submitted values into user data with no cleaning of any kind, which is a stored cross-site scripting hole on the profile screen. Each field type is now cleaned on its own terms.
    • FixedSmaller corrections across the rest: the Shortcode Generator wrote callbacks that never returned their output, the Register Image Size Generator registered sizes too early in the load for them to take effect, the Admin Notice Generator's Danger option emitted a class WordPress has no styling for, and the Dashboard Widget Generator rewrote one of WordPress's internal arrays instead of passing the priority argument added in 5.6.
    • FixedOpening a snippet you had saved before a field was added to its form blanked the page instead of loading it.
    • AddedEnqueue Scripts can set a loading strategy, so a script can be deferred or loaded asynchronously without hand-editing the markup afterwards.
    • AddedThe Plugin Header Generator and Theme Header Generator cover the fields WordPress reads today: the minimum WordPress and PHP versions, required plugins, the update URI, and the version tested up to.
    • AddedAsset paths on both enqueue generators can now be resolved against a child theme or a plugin, not only the parent theme.
    • ChangedGenerated code follows current WordPress practice throughout: permission checks before saving, input cleaned per field type, and the modern media picker in place of a pattern that reached into the editor's own.
    • ChangedEvery generator's output is now run through PHP itself before release, across each branch the forms can take. The snippets that had been fatal on paste were passing the old tests, because a test that records what a generator emits cannot tell correct code from code that will not compile.
  22. 3.0.0

    Rebuilt, top to bottom

    • ChangedRebuilt the entire interface on a new design system — a monochrome, hairline-ruled layout with a bounded content column, built on SevenUI primitives.
    • ChangedRedesigned every generator page as a split workspace: the fields on the left, the generated PHP on the right, staying in view as you scroll.
    • ChangedRewrote the landing page as an index of the generators rather than a brochure, so the tools are reachable from the first screen.
    • ChangedMigrated to the Next.js App Router, moving page metadata to the server. Shared links now unfurl with a title, description and image instead of a bare URL.
    • ChangedModernised the stack: Next.js 16, React 19, Tailwind CSS 4 and a syntax highlighter that ships only the PHP grammar instead of dozens of unused ones.
    • AddedPublished this changelog on the site itself, alongside a written privacy policy and terms of service.
    • AddedA skip-to-content link, and a real heading on every generator page. Assistive technology previously had nothing to announce as the subject of a generator, and keyboard users had to walk the whole navigation to reach the fields.
  23. 2.7.0

    Every generator is now free

    • ChangedDropped the paid tier. The whole catalogue became free to use, with no plan to pick and no limit on how much you generate.
  24. 2.6.0

    Elementor plugins

    • AddedThe Elementor Plugin Generator scaffolds a working Elementor plugin rather than a single file: a widget and a settings page, already wired to each other. It is meant as the starting point you build your own plugin on top of.
  25. 2.5.0

    Scheduled tasks

    • AddedThe Cron Job Generator writes WordPress cron code — the scheduling call, the interval it runs on, and the callback that does the work. It covers the part people most often get subtly wrong: registering the event once rather than on every page load.
  26. 2.4.0

    Child themes

    • AddedThe Child Theme Generator produces the two pieces a child theme needs to inherit from its parent correctly: the stylesheet header naming the template, and the enqueue that loads the parent's styles in the right order.
  27. 2.3.1

    A NEW badge in the catalogue

    • AddedRecently added generators now carry a NEW badge, so a returning visitor can see what has landed since their last visit without reading the whole list.
  28. 2.3.0

    Custom user roles

    • AddedThe Custom User Role Generator builds a role for your site: give it a name and choose the capabilities it holds, and it writes the registration code with each capability spelled out rather than copied from another role.
  29. 2.2.0

    Hooks in a few clicks

    • AddedThe Hook Generator writes actions and filters — the registration call, the priority, and a callback whose signature matches the argument count you asked for. Mismatched argument counts are among the quietest bugs in WordPress, and this removes the chance of one.
  30. 2.1.0

    Custom post statuses

    • AddedThe Post Status Generator registers statuses beyond the built-in draft and publish, with their labels and their visibility in the admin list filled in.
  31. 2.0.2

    User profile fields fix

  32. 2.0.0

    Rebuilt from scratch

    • ChangedThe whole site was redeveloped from the ground up on top of the original version — same idea, rewritten rather than patched.
  33. 1.1.3

    Buy Me a Coffee

    • AddedA Buy Me a Coffee link, added temporarily as a way for people to support the project directly.
  34. 1.1.2

    Ads, to keep the lights on

    • AddedAdvertising was introduced so that development could continue. The generators themselves stayed free and unchanged.
  35. 1.1.1

    Tag input reworked

    • ChangedReworked the tag input, the control the generators use wherever a field takes a list of values rather than a single one.
  36. 1.1.0

    Save and edit your snippets

    • AddedGenerator output could now be saved to an account and reopened later with its fields still filled in, so a snippet could be adjusted instead of rebuilt from memory.
  37. 1.0.0

    Released, free

    • AddedThe first public release. WP Skills started from a simple observation: WordPress developers spend a great deal of time retyping PHP they have already written, and a form can do that part for them.

Want something the generators do not cover yet? Ask on the contact page, or read more about the project.