Get Your Free Brand/Project Consultation Today and Save 10% on Your First Purchase

Mastering WordPress Plugin Development: Hooks, Filters & Pro Coding Secrets (Ep 1)

Key Insights & Takeaways

  1. Hooks and Filters are the Backbone of Plugin Logic
    WordPress allows deep customization through “hooks” (actions) that respond to events, and “filters” that allow you to modify content or data before it’s rendered.
  2. Metadata Matters for Plugin Recognition
    Proper plugin metadata (name, version, description, author, URL) is critical not only for registration but for maintainability, updates, and professionalism.
  3. Hook Priorities Control Execution Order
    Lower numbers execute first; understanding priority is crucial when managing dependency between functions, scripts, and themes.
  4. Conditional Hooks Save Performance
    Using is_single(), is_home(), is_admin() and similar conditionals ensures that actions and filters only run when relevant, improving both speed and logic clarity.
  5. Custom Hooks and Filters Create Extensibility
    Developers can and should create their own do_action and apply_filters for other devs to hook into, allowing clean modular architecture and collaboration.
  6. Proper Use of Closures, Classes & OOP Structures Enhance Maintainability
    Instead of global functions, using classes and constructors to organize hooks and filters leads to scalable, organized codebases.
  7. Logging and Debugging Hooks is Crucial
    Tools like doing_action, get_current_filter, and logging the global $wp_filter are vital for debugging and plugin conflict resolution.

So no matter how many stupid marketers are trying to pop and convince you that everything is AI and everything is going to be taken over by AI, it’s simply not the case. And if you’re in the real world, you know, you know that pretty much more than half of the entire internet, if not a much higher number, is run on WordPress.

And if you’re a dev, you’re definitely going to be at some point where you could have to create a WordPress plugin, which is probably why you clicked on this video. So in this kind of short series, this is video number one of what is basically going to be five videos. And I’d like to get your feedback like please, if you’re watching this, look at the style.

I’m still kind of figuring out like a method that it works for me. So, most of the videos will shortly sort of be the same because I kind of shot them all together. But then here there might be some with a little bit of a different approach. Do check them. Let me know what you think. Let me know what you think.

I can improve on. Or, you know, that’s just like a waste of time. It’s unnecessary. And I’d like to know, would you be interested in watching these videos more if they were like, live sessions like debugging and really kind of building something? Do you like this kind of tutorial style that I’m doing with this one? Do you like the screencast versus, you know, talking?

I’d love to get your feedback really, just so I can improve this and I can make it more enjoyable for you to watch, because ultimately I’m making this for you. I’m making this so you’ve got kind of access to great ideas, and it’s stuff that I wish, you know, when I had to go through all that documentation, all those things and troubleshooting those, I’m hoping some of that knowledge comes across.

Speed your development up or just helps you kind of get to a problem quickly to code to solve it. So without any further ado, let’s get into episode one of the series.

Okay, great. We’re going to start off super simple today, essentially getting a plug in. There’s a couple of primary things that needs to be in place or you need to understand number. Number one is how do we actually get that code that we wrote to be recognized by WordPress. What is the metadata around that. And I’m not going to cover everything in that.

I’m going to give you the essentials of what you need. And then what are hooks and what are filters. Because those are the things you’re going to be working with the most. Essentially, hooks is any time WordPress fires will certain or renders of certain points inside of its code, it’s going to fire off these hooks, for example, when they hit or runs, when the footer runs or any of those things, any kind of activity, when the admin area kind of initializes and we can fire off our functions in conjunction with that to do something at the time that that happens.

So a great way to kind of inject some stuff or check for some behavior, or call an API or set up a database or whatever. And then there are filters. So filters is whenever there’s certain content or areas that’s appearing on the website, such as the footer bar or any kind of area of the site that’s rendering a filter basically sends you that data, and you can grab that data inside of your filter using the Add Filters method.

And with the Add Folders method, you can very easily just get the data and manipulate that data before sending it back to WordPress in order to finish the processing of it. So for example, if we’re looking at a post example, we can get that actual post and we can append something to the bottom. We can look for certain words in it and take those out.

And we can run regular anything you want to do from a PHP logical point of view. You can do then with a filter. So if a default naming convention you need to change, you want to add emoticons. Whatever you need to do, you need to change a currency or a language structure or something like that. You can do all of that with these processes.

Now let’s get into some code. Okay, short and simple of it. If you want to learn to understand hooks and filters, they’re they’re essentially two major commands. So if we’re trying to hook into any events inside of WordPress, such as when the header loads or in the footer loads or any other thing that would be add, underscore, hook, and then the hook that we want to lift into, and then the callback function on our site.

And the exact same thing is for filters it’s add underscore filter. Then the filter we want to hook into. And then our callback function. The core difference is that our cool back function is also going to receive a value, which is the data that we are trying to filter against. So for example, if you’re trying to do it against the posts, that post is actually going to be passed in and we can then take that data, add onto it, manipulated, do whatever we want to and then return it to WordPress in order to display it.

Then from there. So those are the kind of two major things. But before we even get there, we actually need to make sure that WordPress recognizes our plugin and that requires us to actually have the appropriate metadata for WordPress to register it. So let’s look at that first. Okay. So what I’ve got here is my starter kind of piece.

Now this is set up in a way. So I’m basically going to be using this just as an index guide. And I’m going to create my kind of logic here. As you can see, I’ve already gone ahead and kind of scaffolded all of this out so I don’t have to like hand code everything while you watch and we just waste time.

So there are a couple of so this is essentially the very first thing WordPress looks at there. Let’s just say the first thing WordPress is going to look at is that you have a qualified kind of plugin name that’s not going to compete with other plugins. So you really so you really want to make sure that when you are defining your plugin name that it’s unique.

So maybe start off with your company, your product and something like that. In a sequence. And then what you really want to do is you want to try and make your first opening file the same name as your plugins. It’s just a convention that, you know, everyone’s familiar of. So once you’re inside of that plugin file, though, that’s where the very first thing at the top of the file needs to be your metadata.

So that metadata there has a few required fields. For example, we do need to know what the plugin is actually going to be called because that has to show up inside of WordPress. We also need to know what version your plugin is in. Now that’s the absolute minimum requirement of what you have to have in order to get your plugin showing up, but that’s really not the complete process.

It’s really, you know, pretty bad. I would even personally go ahead and for myself, I would make the description also required. And I would also make the author required because, I mean, you want bragging rights don’t you? This is your work. So I would definitely go and make that, at least for myself personally required. And also, if you do want to make the URL that’s going to be linked into that or linked to your name, do the same thing.

There and you want to also like have a location. And again we will need this later is the update URL. That’s where they’ll give the information for the update. So core ones let’s go over them. Firstly the plugin where the plugin is kind of located or at least a website for that plugin. If you’re going to have one description, what version you’re currently in for your plugin, the author name, author URL that’s your own unique URL.

What’s a text domain is so text domain and domain path. This is really for translation. So you’ll see oftentimes that underscore before name. So if you’re using translations in your files that’s where that’s going to be relative. The license again not required but kind of good to have in there along with reference to that license. And then these are quite useful because oftentimes these would be used by the plugin store to determine if your plugin is viable for the current version of WordPress.

So requires at least. And also if your PHP. So then there is your updated. You are also where that is. We’re not really going to cover that in the series. However, if it’s something you are really interested in, please let me know in the comments. And we can always look at adding another video onto the series, as there are specific requests from you and we build it out like that.

Any tags that you want these will be useful for searching and then the network option if this is going to be used as a multi-site plugin, and also not something that we’re going to be using. And if you’ve got any other contributors that you’re working with. So that’s the first kind of single most kind of starting part with your plugin before you even get anything up and running.

So let’s just quickly look at where that shows up. If you don’t know already. Obviously, this information that we have here is essentially what’s going to show up if I’m in my plugins area, the plugin name, the description, the version, the author, and then the plugin website, this is all kind of useful information that you want to have on it, but obviously there is more.

You can expand this out. You can include buttons and links and all kinds of things on that. So you’re not limited by just that. And I think then the only other thing we’ve got to be aware of is there’s obviously multiple ways you can add the plugin. So when you’re going to upload it via the interface, that obviously needs to be as a zip file with the content that we just kind of described there inside of the reach of the zip.

But alternatively, while you’re in dev mode, you can just manually upload that as well to your environment or set it up, as long as you’re making sure you’re putting it inside of the WP contents folder, forward slash plugins and your folder is in there within its folder name, because that’s essentially what the upload plugin is going to do.

It’s just going to upload that zip, unzip it to that folder location. So that’s also how it’s going to determine if an existing version of the plugin is there and if it should update that or just leave it as is.

Okay. But our focus again here is action. So let’s get into action. So all I’ve got here is I’ve just got a path to where my plugin is and I’m requiring that. So I’m passing in the fall. So that’s where I am or the current directory of where I am. And then I’m going to go to chapters action.

And that’s just the link that I’ve pulled up. So I’ve added a whole bunch of comments here for you, along with a whole bunch of like little kind of examples and stuff. And that’s because there is a repo linked to this. So you can just download this code. And that’s really just because sometimes it’s useful. You want to just see maybe there is a case sensitive issue or there’s a spelling issue or something in your code and you want to be able to compare that.

So I’ve got you for that. So you can check that out. And again I’m doing this now is kind of like a walkthrough. If you would rather see me live code this or anything like that, do let me know. And maybe some debugging processes and stuff as I hit them. That’s also an option, but I’d love to hear from you.

Okay, so we’re going to look at a few things. So we’re going to look at action hooks folders. We’re going to be looking at the priorities inside of those passing parameters into them. We’re also going to be looking at creating our custom hooks, as well as removing hooks that we might be in conflict with. And then also we’re also going to be looking at just adding conditions inside of those hooks.

When we want to fire them off only on certain pages or by certain walls. So let’s get into that quickly way. So firstly, when we come to hooks, like some of the available commands and queries that we’ve got is the add underscore hook. And again this takes in a hook a callback function, the priority in terms of a number and any arguments that needs to be passed into that.

And the exact same is true for when we’re adding a filter as and when we’re removing a hook or a filter, then we are only going to pass in the hook that we’re referencing the func, the callback function, and the priority. And what point do we want to remove that? Okay, then we have the do action and apply filters.

That’s usually done by the WordPress course. Initially you just fires off any actions or any callbacks linked to those specific filters. So maybe something hasn’t fired over any problems of that. And then you can kind of force that to fire off. Again. We have we have act has action and half has smolder. That’s just a check. Do these exist.

That’s very useful especially if you’re working with other plugins. And you want to check if maybe a specific function is out of WooCommerce or Learn Press or something is available. We’ve got to get current action to check what the current action is. And we also have doing action so we can see if a current action or filter is currently in progress.

Okay. So let’s look at that in action quickly. Let’s just say I want to fire over my function the moment the WordPress footer looks, hooks out. And that’s quite simple. I can simply pass the WP underscore footer in order to get access to win. That fires off, that hook fires off, and then I fire off my callback function.

And again, the parameters of priority and arguments, those are optional parameters. And here all I’ve got is I’m doing firing off. I point that out. So that’s that call back to my function name or my method’s name. And then inside of this. Now for example, here I’m already applying a conditional. I’m basically saying is underscore admits. In other words, are we in the admin space here.

And if we’re not in the admin space then I want to just echo all of this. Thanks for visiting our site. So if I go to my little snippet here and now, for example, I just go over to loading the main web page and I scroll all the way down. I mean, it’s the absolute do nothing because I have to first activate my plugin.

So let’s activate the plugin and then let’s refresh the page, scroll all the way down and then see at the bottom. Thank you for visiting my site or our site. Okay, cool. Obviously the next one up is the priority is that we can also set. So in this example we’re going to fire off one in conjunction with the head.

So VP underscore head is the hook that we’re going to fire often. So we can get into the quick inner. And here are my functions. No core difference here is the parameter that I’m passing in. And seeing the first one I’m passing in a five. And the second one I’m passing in a 15. And now you can see I’m pretty much passing these in as comments.

So we can just put them up, put them in the HTML as comments. And if you look at this, we basically anything in 1 to 5 is going to execute very early in ten will be the default priority. Hence that if you don’t pass in a variable, that’s essentially what we’re going to be getting. And then 15 to 20 is a later execution.

So that’ll execute after the main code. It’s kind of I’ll put it so it’ll come afterwards. And then a 99 100. Anything like that is going to be very late in the execution. So let’s quickly look at example. This is already in and up in our plugin. So if I go to my page and again because that is now a comment off a HTML, I’m going to go control you in order to get to my source code.

And then if I just look here, I can see on line eight there is my very early, front end, and then there is my later content that fires off. So this is this is the default. It’s 15 and this is my default. Whereas if I’m looking for my late and then going over to 15, my party 15, it’s only going to be firing off here at line 242.

Okay. So versus where my early where my early content on five was firing off at line 12 already. So you want to be careful on how you utilize that. And it really depends on when you need to execute it. So maybe there’s other scripts like the CSS or something like that that you need to load up earlier. And in those cases it would be useful to use the priority a little bit later down.

Next up is passing multiple parameters into your hook into your hooks. So essentially certain hooks or filters have parameters available to them. And it’s almost like in JavaScript when you’re breaking out certain parameters instead of just getting everything. So in this instance, I’m passing through the number three and I’m exploding essentially those to be kind of core parameters that become available.

Now these will be different depending on what you’re hooking into or what you’re filtering into. So you do want to check the documentation for what is available in terms of that. So in this case I’m passing in the post ID the post, and the update. Now I’m able to use that because I can. Now I can say based upon the post I’m looking for the attribute posts status.

Is that equal to published? And then I’m making sure this is not a revision pass, and I’m passing in the post ID to check if it is and if it’s not. Now I can log whatever I want to do, or I can do whatever logic I wanted, but essentially now I have those additional parameters available for me that I can check and work against.

And again, they’ll be different based upon whatever section you are working in or what kind of hope there is. So you might just want to check the WordPress Codex for that. Now, there are several commonly used WordPress filters and I hope that you can connect with. For example, there are the lifecycle hooks. Now these include it hooks like in its WP load template redirect wp head, wp footer and and wp underscore, enqueue scripts as well as for example, shutdown.

That is essentially when the script finishes execution. Now don’t worry, it’s not going to veer off off to that. But just before that essentially stops, we also have admin hooks like admin underscore and it’s been underscore menu underscore inc few scripts for admin underscore notices underscore footer and the line. We also have hooks for the user specifically like WP underscore login or underscore logout user underscore, register profile, underscore, update and delete, underscore user to name a few.

Well, something or hooks related to the theme, such as after underscore, setup, underscore, theme, switch, underscore theme, and widget underscore init. As a couple of examples, we also have your Ajax hooks. So you see a lot of those have a lot of front end kind of tools. And those would fall into two predominant categories. So WP underscore Ajax underscore the action because these are custom defined the action that you want to fire Wolf.

And then there is another one which is WP underscore Ajax underscore no priv as a no privilege underscore your action. The core difference between the two of these is that the one is for authenticated users. And the second is for in authenticated users we have other ones. For example like Crohn’s for example, the ability to delete the gone with WP underscore scheduled underscore delete, as well as auto cleaning up drafts, as well as kind of our own custom hooks that we can also place in terms of that.

Now, when it comes to Folgers, we’re automatically going to get the contents related to the folder that we can pass in. We also break that up into parts, but let’s look at very quickly how we can, for example, modify some content on the post area.

So first we get for example the the underscore content folder. Now this is this is content that’s going to be available in all areas exposed single pages all kinds of things like that because it’s content hook. So when we’re doing that we really want to make sure that we are filtering or adding some conditions to make sure we’re getting it for the area that we want.

So we’re just not firing it over everywhere. So in this case, I’m passing it to my Michael back function. And obviously I’m getting its content available. Filter and from here on I’m gonna add in some conditionals, not a conditionals. Here a very simple I’m saying is single. In other words, it’s not like an archive page, like where I see all my posts.

I’m using the is single. In other words, not. I’m not looking for if it is a page or an archive. I’m basically just saying, is this a single post that I’m on? And then I’m going to say in the loop. In other words, is this I want this firing off inside of the loop of the information being output.

And is this part of the main query. So which is the query that will be to be filtering for the post. So all I’m doing then is I’m not overwriting my data and just appending. As you can see another little div block in terms of that. And then I’m just and this is the important thing. You always want to return that data.

Otherwise you’re not. WordPress is not going to be able to render the content that actually is supposed to be there. So in this case, if I now go over to my blog, I’m going to see absolutely nothing because I don’t have any blog posts, but I can go ahead and I can go add a blog post, say Hello World from Funky Content.

I’m going to publish that and this gives a post. And now we’ve got our additional thank you for you for thank you for reading. Share your thoughts in your comments, which is what we’re outputting here inside of our other piece on. Obviously now I’ve passed in my cursor so I can modify that, perhaps by using WP Inc scripts to load up some scripts, depending on obviously the control I have on the plugin that I’m doing.

For example, if you just go to overwrite a function file, you don’t have all the power. Maybe you’re going to do that that way. Otherwise, if it’s your own plugin, you could do a lot more. Now again, in the same, I can have ones where I break up the parameters, in which case I’m just passing in title and post.

I’m doing the exact same thing as I was doing there. I’m looking at that and I’m adding this little emoticon into it. So hence why when I actually look at my Hello World, I’ve got my little emoticon that’s popped in there because I’ve just taken the title and I’ve just prepended to the title that little emoticon. Something else I can do is one of those filters.

There’s also the actual queries that are running on those pages. So let’s just say I want to limit the amount of queries, maybe for posts or anything like that. I can always do that by just overriding the query. So in this case I’m getting the pre underscore get underscore parts. In other words before the posts are firing off, passing that over and passing the query in.

And now I can make sure that first I’m not in the admin. This is the main query for that section. And then I want to maybe add some conditions. Firstly am I on the home page. And if I’m on the home page make sure that I set that query specifically this the posts per page parameter on that query to be limited to.

Fine. And then maybe I go of my uncategorized section. I want to make sure that none of those pages show up. So I can also say from the query.

get the is category and then if it is set, if the category is set, then I want to remove maybe number one from that Cat query that’s inside of the query.

So I can add that in. Now again just like before folks, a full two should also have a range of options to choose from, such as the content folders we just talked about. And that could be the underscore content. The underscore title, the underscore expert, the underscore excerpt w underscore title for the title tag, the get underscore, the underscore, the WB underscore trim, underscore, excerpt, exit, underscore length, and expert underscore morphs such as when you want to change that read more.

Link to. For example, say something else like check our post or whatever. We also have template folder such as body underscore clause, which adds a tag to the body class post underscore class, which allows us to add classes to the post container single underscore template which which allows us to modify the single post template page. Underscore template which allows the modify page templates.

Archive underscore template. Obviously for the archive search underscore template, modify the search for a for underscore template, which allows us to do modifications on for a for page. We also have the navigation folder such as nav underscore, menu, underscore, CSS, underscore class, wp underscore nav underscore items. This allows us to modify the menu items wp underscore page. Underscore menu allows us obviously to modify the menu wp underscore list underscore page which allows us to modify a page listing.

Then we get the queries that run on those pages. For example, the one you just saw pre underscore get underscore posts allows us to modify the main query underneath the post before it executes. With that, we also have post underscore per page. We have query underscore var for when we want to add additional query variables to that. And then we have request in case we want to actually modify the entire request being passed down.

When it comes to user authentication we have things like authenticate WP underscore log and underscore error. So we can modify those pesky error messages in underscore redirect in case we want to change that log out underscore redirect. Obviously for when their user is logging out use their underscore contact method. So if you want to change the contact method get underscore avatar.

If you want to override, for example, the standard Gravatar avatar avatars that show up adding filters admin underscore, footer, update, footer, plug in, underscore action, and the list goes on. Email. For filters, we can modify the email. We can modify the from. We can modify the from name. We can modify the content type, sanitize file names, which is very useful when we come to the security folders w underscore Di handlers.

How do we modify those error messages? Map underscore made a cap. So how do we modify. These are modifiers for capability mapping editable roles. We can modify those widget folders. So we have widget underscore title. We can modify dynamics underscore sidebar params so we can pass in or manipulate. So widget display callback. We can also manipulate those. And then we have the ever popular I’m getting internationalization folder.

So those are the language ones. The local the get takes the get takes with context. So if ever we want to change those click outfit change. So now actually there may be a couple of things that are coming in from other plugins or WordPress itself that you don’t necessarily want inside of your application. Perhaps for performance, or maybe it’s contradicting something else that you’re doing and you just want to remove that overhead.

You can simply use the remove underscore action or the remove underscore filter in order to target that specific item. Now what we’re going to do is we’re going to be targeting obviously the hook, but what we’re trying to do is we’re trying to remove a specific function off of that hook that’s usually fired off when to do underscore hooks runs.

So for example, in WordPress itself there is the WordPress generator hook, which gives us the version of the WordPress. Now that’s something you might want to remove, because you don’t want hackers to identify what version of WordPress you’re on just by scraping your HTML. We can do that by doing the remove underscore action on the WP underscore head hook.

And then we can pass in the function that we’re trying to remove from that. So in this case the w underscore generator. Others that you also might want to remove. That’s not always that common use is something like the rest. The really simple discovery the live writer manifest for windows. You could also affect the formatting that it maybe injects inside of the inserts.

And you can also, if you want it to affect the capital data. That just capitalizes the wrong side of the WordPress when it’s found inside of the text message is another filter that’s there. Now, in this case, I’m just firing this off in terms of my in it. So the moment that my WordPress initializes, I’m going to fire off my remove hook function to go ahead and just remove all of those out.

That way I’m firing it off before they all kind of get their opportunity to dance around. Now, naturally, just like other plugins such as WooCommerce or Elementor or any of the others, you may have custom solutions that you want to make available also as hooks, that’s going to fire off in conjunction with other of your activity, such as with WooCommerce, the product page, or any of the others.

The way you would do that is naturally inside of your plugin. You can have a variety of different functions that handles a couple of different solutions, for example, listing product pages or doing anything like those. Now, maybe you have a name structure that comes back and you want to give the user the ability to filter that, or maybe you want to give him a before and after hook on top of an existing function that they can fire off some of their own logic, such as modifying the HTML or anything like that.

So let’s assume we’ve got a function inside of our code called, display custom content. That will be now in this case our custom hooks. So inside of that, what we want to do in order to fire all of the hooks that the user’s not created, aligning to our hooks. So in other words, they are add underscore action, which will then pass in the hook that we’ve created and obviously their function to filter down.

So the way our code reads that up is by using the do after a similar to how WordPress would fire off its hooks. So it’s the do actions will then basically give the name of the hook that we’re letting fire off in that sense, so we can simply define it inside of this area. So here you can see an example where I’m doing the do action and I’m defining a hook for it.

Then I’m going to fire off. And then I’ve got another one at the bottom which I’m doing after the content. And this is inside of my function that I’m outputting. So then as the user or even as myself, if I want to manipulate or reuse that component, I can then just do a normal centered add underscore action pass in the hook that I’ve now defined.

And obviously the callback method that’s going to be used for it in the classic and typical way. So it’s that simple. The same thing is really the case when I want to do filters. So in this case I have a function called get greeting message. And I’m passing in a parameter. Or I’m expecting a parameter there of name.

And then what I’m going to be doing is I’m going to echo whatever that name is going to be. Maybe that’s just how I’m greeting. And obviously you do your own logic. Maybe you’re outputting the user’s name or something like that. And what I want to do is I want the ability to filter this message. So I’m allowing the user to pass in some modifying data, and the same as to do actions to fire off the filters.

I’m going to be using apply underscore filters and apply underscore filters. Here I’ve got my name that I’m giving it. And then the same thing is when the user wants to pass that in, they’re going to add underscore filters in the traditional way that they would normally add a filter or pass a filter. And they’re giving it the name, they’re giving their callback function and they’re giving the value of ten.

Now I’m passing in two because of ultimately making two parameters available for my user to modify. That is, the content in the modifying data. So the content is the data that I’m actually going to be returning, and in the data I’m allowing them to manipulate. So in this case I’m just giving the ability. In this case I’m just overriding that entire message based upon whatever the message is.

So at first it’ll say hello, keeping whatever the default name is. And then just welcome to our site, which is what I’m going to paint onto that. So the take the message and the by filter, simply going to take the message that already is there and append onto it and prepend to it a little bit of more details.

So very quick and simple. You can easily expand your plugins by making it more user friendly for other developers if they want to apply and work with your conditions. Now, speaking of conditions, WordPress is full of a variety of different areas. You obviously don’t want your hooks and your filters affecting absolutely everything, because a lot of those may be in use for other reasons.

And also it’s a lot of overhead on the site that’s also going to affect performance. And you always need to consider the performance of your site. So WordPress gives us a lot of conditional hooks. We’ve already looked at a few of those. Let’s look at a few more. So some of the ones you’re going to use very commonly is the is underscore single.

That’ll be for example single person I’ve already looked at that. We’ve got the is underscore home. See for in the home page is underscore admin. If we’re on the admin page is underscore singular. That would be when we’re looking at a very specific post type such as book which we’re going to create in the series is underscore archive.

That would be the archive. In other words the listing of the posts page is underscore category. So if we’re putting those posts inside of category pages we also then have is underscore. Tag is author. The search is for a form is page when it’s just an actual page and not a post is attachment. If you want to look for attachments and then you can also see things like is user logged in, which is especially useful obviously if you want to load up different functionality or views depending on the users of indication.

And speaking of user authentication, that leads us into current user can. So current user can only give us the access or the watch the privilege of the current user, but we can get the actual current user with WP underscore get current user. Now that gives us a role key inside of it. So we can very easily pass or generate an array with a couple of different roles.

And then we can use that to intersect. So we can double check there for different role authentications. But it’s also another solution that you can use based upon what you want to render, depending on user roles or any other activity that’s available on the user object. If you want to get it back that way, I’ll just add another one.

You can also use closures with your UX. In other words, you don’t have to use a callback or define it to a callback. It’s a very small function. You can very easily just handle that inline. So in this one where I have add action score and I’m trying to target the the WP underscore footer, then I can just pass in a normal function and then inside do whatever I need to do.

So that’s very good. If you just need to do very simple tasks very quickly, although it might not be the best and recommendation, but if you’re testing things, that’s quite a useful way to go about it. Lastly, something you might want to consider is proper good object oriented programing when you’re utilizing your development. So you can also bind all of these specific groups, maybe into specific classes that handle specific areas.

And then you can simply fire off those classes. Now one way of doing that is naturally in the construct function. So whenever I’m defining a normal kind of class and I’m kind of containing inside of all of my elements and all your typical object orientated rules will obviously be applied to that or in your construct method, you can, in very easily just fire off those add actions that you want to in order to sequentially fire off those specific callbacks that you’ve been defined inside of that.

Okay. So that pretty much covers it in the area of hooks and filters and getting you started in terms of those, just a couple of things you always want to keep in mind is number one, keep good prefixes towards what you’re doing. It’s very easy for someone to have a similar name idea that you want. So really consider your name spacing prefixes so they’re specific to you and your organization.

And make sure that carries over down to the function level as well. Not just the name of your plugin, but everything that you’re going to be doing. Because again, similar name, similar places. Make sure your your names you add titles for your hooks as well as for your callbacks are descriptive. They kind of clarify what they do. So it’s easy for someone to read and also say that your code is very fairly well organized, so do take advantage of using clauses and executing them appropriately.

It’s just going to make life a lot easier for other people. Looking at your plugin in the future or working with you, and you’re going to thank yourself later when you get to come back to it. After a year of working on something else for a few months, or every time you get to come back to update it.

So also make sure you add some documentation, maybe a read me and some markdown as well as some good code comments along the way. It’ll make your life easier. It might be a bit more work upfront, but you’re going to thank yourself later on. Remember to use hook priority. Depending on what’s happening, you might be trying to get a function going and it’s just not happening.

And that may just be that another method was running after yours and overriding what you’ve done, or maybe running earlier and removing a hook that you’re depending on. So because of that, not only you should consider the utilization of priorities, but you should also consider using proper error logging so you can log and debug as you go. While you’re debugging your environ, because you don’t know necessarily.

Something may work perfectly in your environment, your test spaces, and then you share it with a client, or you share up in the public, and then it fails in other environments. You haven’t tested against those themes. You haven’t tested against those other plugins that, again, may affect filter and hook system in some other way. So writing things to logs in a way that you can get those logs is going to be a lifesaver.

And then just, you know, look at the security again, make sure you’re sanitizing any input data. You’re validating user activities against the permission or the roles of that user. You’re using your conditions appropriately so you’re not firing Wolf in all the wrong spaces. So a last thing I’m going to leave you with on this one before we hit out back to the desk is there’s a couple of useful little debugging tools that you might want to use, which is the has action.

So we can see if that action actually is available or still available. It may be dropped between versions. We also have get current action and current filter, so we can find out what the current action and current filter is that we’re firing in. And then we have a benefit of a something called a doing underscore action. So doing under score action can be used to print out what actions happening in what time.

So if need to figure out what’s firing off when you can use doing underscore action against a certain to figure out exactly what’s going on inside of it. And at any point if you are uncertain of what is available in that version, you can do the dub. You can pass in the global WP underscore filter. And then from that you can var dump any items inside of it in order to pick what’s actually firing off against that, such as the init function or the WP or any of the other hooks, which are then going to be available to you and show you what else is picking up.

This way you can write conditional logic against those to prevent or notify the user of a conflicting plugin or function, or something that may have an impact against your development. I hope you learned something and I’ll see you in the next lesson. Okay, I hope that was useful. You learned something again, if you can kind of share some suggestions on how I can kind of get better at this, I’d love to hear it because these are my first, like longer tutorials.

And I’m going to keep pumping up some cool stuff. So if you also got other subjects or topics you want to hear about, let me know and also check link down below. So links for the series if they’re available at the time of you watching this video, so you can get the whole collection of them.

<?php

/**
 * CHAPTER 1: ACTIONS AND FILTERS (HOOKS)
 * 
 * This chapter demonstrates WordPress hooks system - the foundation of WordPress extensibility.
 * Hooks allow you to "hook into" WordPress at specific points to execute custom code.
 * 
 * Key concepts covered:
 * - Action hooks: Execute custom code at specific points
 * - Filter hooks: Modify data before it's displayed or processed
 * - Hook priority and parameters
 * - Creating custom hooks
 * - Removing hooks
 * - Conditional hook execution
 * 
 * THE WORDPRESS HOOKS SYSTEM:
 * 
 * WordPress uses an event-driven architecture where code execution happens at specific
 * points called "hooks". There are two types of hooks:
 * 
 * 1. ACTION HOOKS (do_action):
 *    - Execute custom code at specific points
 *    - Don't return values
 *    - Used for: adding functionality, outputting content, performing tasks
 * 
 * 2. FILTER HOOKS (apply_filters):
 *    - Modify data before it's used
 *    - Must return modified data
 *    - Used for: changing content, modifying settings, altering behavior
 * 
 * Hook Functions:
 * - add_action($hook, $function, $priority, $args): Attach function to action hook
 * - add_filter($hook, $function, $priority, $args): Attach function to filter hook
 * - remove_action($hook, $function, $priority): Remove function from action hook
 * - remove_filter($hook, $function, $priority): Remove function from filter hook
 * - do_action($hook, $args...): Execute all functions attached to action hook
 * - apply_filters($hook, $value, $args...): Apply all functions attached to filter hook
 * - has_action($hook, $function): Check if function is attached to action hook
 * - has_filter($hook, $function): Check if function is attached to filter hook
 * - current_action(): Get the current action hook being executed
 * - current_filter(): Get the current filter hook being executed
 * - doing_action($hook): Check if specific action is currently being executed
 * - doing_filter($hook): Check if specific filter is currently being executed
 * 
 * Hook Parameters:
 * - $hook: The hook name (string)
 * - $function: Function name or array(object, method) or closure
 * - $priority: Execution priority (integer, default 10, lower = earlier)
 * - $args: Number of arguments the function accepts (default 1)
 */

// SECTION 1: ACTION HOOKS
// Actions execute code at specific points without returning values

/**
 * Example 1: Simple action hook
 * 
 * This action runs when the footer of the site is loaded and displays a message.
 * 
 * wp_footer hook is called in the footer.php template of most themes.
 * It's commonly used for:
 * - Adding tracking codes (Google Analytics, etc.)
 * - Inserting JavaScript
 * - Adding footer widgets or content
 * - Loading scripts that should run after page content
 */
add_action('wp_footer', 'hth_footer_message');
function hth_footer_message() {
    // Only show on frontend, not in admin
    if (!is_admin()) {
        echo "<p style='text-align:center; margin-top:20px; font-style:italic;'>Thanks for visiting our site!</p>";
    }
}

/**
 * Example 2: Action with priority
 * 
 * This demonstrates how priority affects execution order.
 * Lower priority numbers execute first.
 * 
 * Default priority is 10. Common priorities:
 * - 1-5: Very early execution
 * - 10: Default priority
 * - 15-20: Later execution
 * - 99-100: Very late execution
 */
add_action('wp_head', 'hth_early_head_content', 5);
function hth_early_head_content() {
    echo "<!-- Early head content (priority 5) -->\n";
}

add_action('wp_head', 'hth_late_head_content', 15);
function hth_late_head_content() {
    echo "<!-- Late head content (priority 15) -->\n";
}

/**
 * Example 3: Action with multiple parameters
 * 
 * This demonstrates how to accept multiple parameters in hook functions.
 * The fourth parameter in add_action() specifies how many arguments to accept.
 */
add_action('save_post', 'hth_post_saved_notification', 10, 3);
function hth_post_saved_notification($post_id, $post, $update) {
    // Only run for published posts, not drafts or auto-saves
    if ($post->post_status === 'publish' && !wp_is_post_revision($post_id)) {
        // Log the post save event
        error_log("Post saved: {$post->post_title} (ID: {$post_id}, Updated: " . ($update ? 'Yes' : 'No') . ")");
    }
}

/**
 * COMMONLY USED ACTION HOOKS:
 * 
 * WordPress Lifecycle:
 * - init: After WordPress loads, before headers sent
 * - wp_loaded: After WordPress fully loads
 * - template_redirect: Before template is loaded
 * - wp_head: In HTML <head> section
 * - wp_footer: In HTML footer
 * - wp_enqueue_scripts: Proper place to enqueue scripts/styles
 * - shutdown: When PHP execution ends
 * 
 * Admin Hooks:
 * - admin_init: Every admin page load
 * - admin_menu: Add admin menu items
 * - admin_enqueue_scripts: Enqueue admin scripts/styles
 * - admin_notices: Display admin notices
 * - admin_footer: Admin footer
 * 
 * Post/Page Hooks:
 * - save_post: When post is saved
 * - delete_post: When post is deleted
 * - wp_insert_post: When new post is created
 * - before_delete_post: Before post deletion
 * - transition_post_status: When post status changes
 * 
 * User Hooks:
 * - wp_login: When user logs in
 * - wp_logout: When user logs out
 * - user_register: When new user registers
 * - profile_update: When user profile is updated
 * - delete_user: When user is deleted
 * 
 * Comment Hooks:
 * - comment_post: When comment is posted
 * - wp_insert_comment: When comment is inserted
 * - delete_comment: When comment is deleted
 * - comment_approved: When comment is approved
 * 
 * Theme Hooks:
 * - after_setup_theme: After theme loads
 * - switch_theme: When theme is switched
 * - widgets_init: When widgets are initialized
 * 
 * AJAX Hooks:
 * - wp_ajax_{action}: AJAX for logged-in users
 * - wp_ajax_nopriv_{action}: AJAX for non-logged-in users
 * 
 * Cron Hooks:
 * - wp_scheduled_delete: Clean up scheduled posts
 * - wp_scheduled_auto_draft_delete: Clean up auto-drafts
 * - {custom_cron_hook}: Custom scheduled events
 */

// SECTION 2: FILTER HOOKS
// Filters modify data before it's displayed or processed

/**
 * Example 1: Content filter
 * 
 * This filter modifies post content before it's displayed.
 * Filters must always return the modified data.
 * 
 * the_content filter is one of the most commonly used filters.
 * It's applied to post content before display.
 */
add_filter('the_content', 'hth_append_text_to_post');
function hth_append_text_to_post($content) {
    // Only modify content on single posts, not pages or archives
    if (is_single() && in_the_loop() && is_main_query()) {
        $content .= '<div class="post-footer-note">';
        $content .= '<p><em>Thank you for reading! Share your thoughts in the comments below.</em></p>';
        $content .= '</div>';
    }
    return $content; // Always return the content!
}

/**
 * Example 2: Title filter with parameters
 * 
 * This filter demonstrates:
 * - Using multiple parameters in filters
 * - Conditional filtering based on context
 * - Modifying data based on post type
 */
add_filter('the_title', 'hth_modify_post_title', 10, 2);
function hth_modify_post_title($title, $post_id) {
    // Only modify titles on frontend single posts
    if (is_single() && !is_admin() && in_the_loop()) {
        $post = get_post($post_id);
        if ($post && $post->post_type === 'post') {
            $title = '📖 ' . $title; // Add an emoji prefix
        }
    }
    return $title;
}

/**
 * Example 3: Query modification filter
 * 
 * This demonstrates how to modify WordPress queries before they execute.
 * This is very powerful for changing how content is displayed.
 */
add_filter('pre_get_posts', 'hth_modify_main_query');
function hth_modify_main_query($query) {
    // Only modify the main query on the frontend
    if (!is_admin() && $query->is_main_query()) {
        // On the home page, show only 5 posts
        if ($query->is_home()) {
            $query->set('posts_per_page', 5);
        }
        
        // On category pages, exclude posts from category ID 1
        if ($query->is_category()) {
            $query->set('cat', '-1');
        }
    }
}

/**
 * COMMONLY USED FILTER HOOKS:
 * 
 * Content Filters:
 * - the_content: Modify post/page content before display
 * - the_title: Modify post/page titles
 * - the_excerpt: Modify post excerpts
 * - wp_title: Modify page title in <title> tag
 * - get_the_excerpt: Modify excerpt retrieval
 * - wp_trim_excerpt: Modify excerpt trimming
 * - excerpt_length: Modify excerpt word count
 * - excerpt_more: Modify "read more" text
 * 
 * Template Filters:
 * - body_class: Add classes to body tag
 * - post_class: Add classes to post containers
 * - single_template: Modify single post template
 * - page_template: Modify page template
 * - archive_template: Modify archive template
 * - search_template: Modify search template
 * - 404_template: Modify 404 template
 * 
 * Navigation Filters:
 * - nav_menu_css_class: Modify menu CSS classes
 * - wp_nav_menu_items: Modify menu items
 * - wp_page_menu: Modify page menu
 * - wp_list_pages: Modify page listing
 * 
 * Query Filters:
 * - pre_get_posts: Modify queries before execution
 * - posts_per_page: Modify posts per page
 * - query_vars: Add custom query variables
 * - request: Modify query request
 * 
 * User & Authentication Filters:
 * - authenticate: Modify user authentication
 * - wp_login_errors: Modify login error messages
 * - login_redirect: Modify login redirect URL
 * - logout_redirect: Modify logout redirect URL
 * - user_contactmethods: Modify user contact methods
 * - get_avatar: Modify user avatars
 * 
 * Admin Filters:
 * - admin_footer_text: Modify admin footer text
 * - update_footer: Modify admin version text
 * - plugin_action_links: Modify plugin action links
 * - manage_posts_columns: Modify post list columns
 * - manage_pages_columns: Modify page list columns
 * 
 * Media Filters:
 * - upload_mimes: Modify allowed file types
 * - wp_get_attachment_url: Modify attachment URLs
 * - image_send_to_editor: Modify image insertion
 * - media_upload_tabs: Modify media upload tabs
 * - attachment_fields_to_edit: Modify attachment fields
 * 
 * Email Filters:
 * - wp_mail: Modify email parameters
 * - wp_mail_from: Modify sender email
 * - wp_mail_from_name: Modify sender name
 * - wp_mail_content_type: Modify email content type
 * 
 * Security Filters:
 * - sanitize_file_name: Modify uploaded file names
 * - wp_die_handler: Modify error handling
 * - map_meta_cap: Modify capability mapping
 * - editable_roles: Modify available user roles
 * 
 * Widget Filters:
 * - widget_title: Modify widget titles
 * - dynamic_sidebar_params: Modify sidebar parameters
 * - widget_display_callback: Modify widget display
 * 
 * Internationalization Filters:
 * - locale: Modify site locale
 * - gettext: Modify translation strings
 * - gettext_with_context: Modify contextual translations
 */

// SECTION 3: REMOVING HOOKS
// Sometimes you need to remove hooks added by WordPress core or other plugins

/**
 * Example of removing actions and filters
 * 
 * This demonstrates how to remove existing hooks.
 * This is useful when you want to disable default WordPress behavior.
 */
function hth_remove_default_hooks() {
    // Remove the WordPress version from head
    remove_action('wp_head', 'wp_generator');
    
    // Remove RSD link from head
    remove_action('wp_head', 'rsd_link');
    
    // Remove Windows Live Writer manifest link
    remove_action('wp_head', 'wlwmanifest_link');
    
    // Remove automatic paragraph formatting from excerpts
    remove_filter('the_excerpt', 'wpautop');
    
    // Remove capital P filter (changes "wordpress" to "WordPress")
    remove_filter('the_content', 'capital_P_dangit', 11);
    remove_filter('the_title', 'capital_P_dangit', 11);
}

// Hook the removal function to init
add_action('init', 'hth_remove_default_hooks');

// SECTION 4: CUSTOM HOOKS
// Creating your own action and filter hooks for extensibility

/**
 * Example of creating a custom action hook
 * 
 * This demonstrates how to create your own hooks that other developers
 * (or your own code) can hook into.
 */
function hth_display_custom_content() {
    echo '<div class="custom-content">';
    
    // Fire a custom action before content
    do_action('hth_before_custom_content');
    
    echo '<h2>Custom Content Section</h2>';
    echo '<p>This is some custom content.</p>';
    
    // Fire a custom action after content
    do_action('hth_after_custom_content');
    
    echo '</div>';
}

/**
 * Example of creating a custom filter hook
 * 
 * This demonstrates how to create filter hooks that allow
 * modification of your plugin's data.
 */
function hth_get_greeting_message($name = 'User') {
    $message = "Hello, {$name}!";
    
    // Apply a custom filter to allow modification
    $message = apply_filters('hth_greeting_message', $message, $name);
    
    return $message;
}

// Examples of hooking into our custom hooks
add_action('hth_before_custom_content', 'hth_add_before_content');
function hth_add_before_content() {
    echo '<p><em>This content was added via custom hook!</em></p>';
}

add_filter('hth_greeting_message', 'hth_modify_greeting', 10, 2);
function hth_modify_greeting($message, $name) {
    return "🎉 " . $message . " Welcome to our site!";
}

// SECTION 5: CONDITIONAL HOOKS
// Executing hooks only under specific conditions

/**
 * Example of conditional hook execution
 * 
 * This demonstrates how to conditionally add hooks based on
 * various WordPress conditions.
 */
function hth_conditional_hooks() {
    // Only add footer script on single posts
    if (is_single()) {
        add_action('wp_footer', 'hth_single_post_footer');
    }

    // Only modify titles on the home page
    if (is_home()) {
        add_filter('the_title', 'hth_home_title_modifier');
    }

    // Only add admin styles in admin area
    if (is_admin()) {
        add_action('admin_enqueue_scripts', 'hth_admin_styles');
    }

    // Only run on specific post types
    if (is_singular('book')) {
        add_action('wp_head', 'hth_book_meta_tags');
    }

    // Only on archive pages
    if (is_archive()) {
        add_action('wp_footer', 'hth_archive_footer');
    }

    // Only on category archive
    if (is_category()) {
        add_action('wp_head', 'hth_category_head');
    }

    // Only on tag archive
    if (is_tag()) {
        add_action('wp_head', 'hth_tag_head');
    }

    // Only on author archive
    if (is_author()) {
        add_action('wp_footer', 'hth_author_footer');
    }

    // Only on search results page
    if (is_search()) {
        add_filter('the_content', 'hth_search_content_modifier');
    }

    // Only on 404 page
    if (is_404()) {
        add_action('wp_footer', 'hth_404_footer');
    }

    // Only on front page
    if (is_front_page()) {
        add_action('wp_head', 'hth_front_page_head');
    }

    // Only on page (not post)
    if (is_page()) {
        add_action('wp_footer', 'hth_page_footer');
    }

    // Only on attachment pages
    if (is_attachment()) {
        add_action('wp_head', 'hth_attachment_head');
    }

    // Only for logged-in users
    if (is_user_logged_in()) {
        add_action('wp_footer', 'hth_logged_in_footer');
    }

    // Only for specific user role (e.g., administrator)
    if (current_user_can('edit_posts')) {
        // multiple roles 
        $user = wp_get_current_user();
        $allowed_roles = array('editor', 'administrator', 'author');
        if (array_intersect($allowed_roles, $user->roles)) {

            add_action('admin_notices', 'hth_admin_notice');
        }
        
    }
}

// Hook to template_redirect (runs after query is determined)
add_action('template_redirect', 'hth_conditional_hooks');

// SECTION 6: ADVANCED HOOK TECHNIQUES
// Advanced patterns and techniques for working with hooks

/**
 * Example of hook priority and execution order
 * 
 * This demonstrates how priority affects execution order
 * and how to ensure your hooks run at the right time.
 */
add_action('wp_head', 'hth_very_early_head', 1);
function hth_very_early_head() {
    echo "<!-- Very early head content -->\n";
}

add_action('wp_head', 'hth_early_head', 5);
function hth_early_head() {
    echo "<!-- Early head content -->\n";
}

add_action('wp_head', 'hth_default_head'); // Default priority 10
function hth_default_head() {
    echo "<!-- Default priority head content -->\n";
}

add_action('wp_head', 'hth_late_head', 20);
function hth_late_head() {
    echo "<!-- Late head content -->\n";
}

/**
 * Example of using closures with hooks
 * 
 * This demonstrates how to use anonymous functions (closures)
 * with WordPress hooks.
 */
add_action('wp_footer', function() {
    echo '<script>console.log("Closure hook executed!");</script>';
});

/**
 * Example of using class methods with hooks
 * 
 * This demonstrates how to hook class methods to WordPress hooks.
 */
class HTH_Hook_Example {
    
    public function __construct() {
        // Hook instance method
        add_action('init', array($this, 'init_method'));
        
        // Hook static method
        add_action('wp_head', array(__CLASS__, 'static_method'));
    }
    
    public function init_method() {
        // Instance method hooked to init
        error_log('Instance method called on init');
    }
    
    public static function static_method() {
        // Static method hooked to wp_head
        echo "<!-- Static method output -->\n";
    }
}

// Instantiate the class to register hooks
new HTH_Hook_Example();

/**
 * BEST PRACTICES FOR HOOKS:
 * 
 * 1. Naming Conventions:
 *    - Prefix all function names with your plugin/theme prefix
 *    - Use descriptive names that explain what the hook does
 *    - Example: 'hth_modify_post_title' instead of 'modify_title'
 * 
 * 2. Hook Priority:
 *    - Use default priority (10) unless you have a specific reason to change it
 *    - Lower numbers run earlier, higher numbers run later
 *    - Common priorities: 1 (very early), 5 (early), 10 (default), 15 (late), 20 (very late)
 * 
 * 3. Performance Considerations:
 *    - Avoid heavy processing in frequently called hooks
 *    - Cache expensive operations when possible
 *    - Use conditional checks to limit hook execution
 * 
 * 4. Security:
 *    - Always sanitize input data in hooks
 *    - Validate user permissions before executing admin hooks
 *    - Escape output data properly
 * 
 * 5. Debugging:
 *    - Use error_log() for debugging hook execution
 *    - Check if hooks are running with has_action() and has_filter()
 *    - Use current_action() and current_filter() to identify context
 * 
 * 6. Documentation:
 *    - Document your custom hooks for other developers
 *    - Include parameter descriptions and examples
 *    - Mention when hooks are fired and what they do
 * 
 * 7. Testing:
 *    - Test hooks in different contexts (admin, frontend, AJAX)
 *    - Test with different user roles and capabilities
 *    - Test hook removal and modification
 * 
 * 8. Compatibility:
 *    - Check if functions exist before using them
 *    - Be aware of WordPress version compatibility
 *    - Consider multisite compatibility
 * 
 * DEBUGGING HOOKS:
 * 
 * // Check if a hook exists
 * if (has_action('init', 'your_function')) {
 *     // Hook is registered
 * }
 * 
 * // Get current action/filter
 * $current_action = current_action();
 * $current_filter = current_filter();
 * 
 * // Check if action is currently executing
 * if (doing_action('init')) {
 *     // We're currently in the init action
 * }
 * 
 * // List all hooks for debugging
 * global $wp_filter;
 * var_dump($wp_filter['init']); // Shows all functions hooked to init
 */
Influencer, Blogger, Vlogger

Crafting Your Personal Brand: A Comprehensive Guide for Influencers

In the realm of influencers, personal branding is the key to standing out and making a lasting impact

Implementing Scrum in Your Tech Startup: A Step-by-Step Guide

In the dynamic landscape of tech startups, efficient project management is the key to success. Scrum, an agile framework,

Nuhuman’s Jeanré Van Zyl Shows Ways To Hack your Biology

Kyla sits down with Jeanré Van Zyl, a regional biohacker well-versed in the field of wellness and technology to find […]

How much will it cost?

It is often quite a simple question, but as the answer unfolds it becomes evident that it is a lot […]

Apple Gule Unplugged: A Candid Conversation with Apple Gule

Born in  Nhlangano, Swaziland, Aphelele Gule, better known as Apple Gule to his loyal fanbase, reaching various genres including house […]

Escape the 9–5: How to Build a Profitable Freelance or Digital Agency Career

1. Mindset Trumps Strategy No matter how many strategies or tutorials are available, real progress is blocked by psychological limitations. […]

Communication Hacks for Agencies, Freelancers & Startups: Save Time & Wow Your Clients

Key Takeaways Find the Right Communication Balance: Avoid overloading clients with unnecessary details or going radio-silent. Give them the info […]

From Legacy to Leadership: CTO Lessons in Building Tech that Actually Works

This is a company that's moved in from the retail space and is now moving into more areas of merchant and payment gateways, with a...