How to be a Freelance WordPress Developer

Hi there! :), your here and I hope your interested in earning money by doing freelance work.

A little bit about me before we get started, I was a freelance developer for more than 3 years and I designed (some) and developed more than 40 websites. My customers were from different countries including Sri Lanka, United States and Singapore. It was hard work managing a regular day job and doing freelance website development as well, but it was worth it. That extra work is what helped me to save enough money to buy my car and also helped me finance my self to move to Singapore when I got my current job here.

So enough about my self, this post is to note down the things I learned the hard way when doing freelance development. Hopefully this will help you who is new to website development learn something and get started. I sincerely hope that you can be successful by following some of the information given here, but I hope you understand that it is actually hard work on your part which will ensure your success.

Ok So lets get to it,

Why WordPress?

Why WordPress? Why not develop websites using HTML and CSS or Drupal or Joomla. The main reason I picked WordPress was because it was very easy to learn. A huge community and lots of tutorials and materials are out there to help you to get started.wordpress-logo

And the other reason is there are lots of jobs available for WordPress. Its easy enough for normal people(Non developers or Muggles 😛 ) to get started using it on their own but just complex enough to prevent them from building a fully customised website. So people are always looking for developers to help them customise their website built using WordPress which they already started on their own or people who want to upgrade and rebuild their old websites.

My speciality was catering to those clients, who wanted to convert their static websites into CMS based website, so that They can manage their website on their own.

There are other reasons, But for me above two were the main reasons.

Types of WordPress Work

Website Setup/ Installation / Maintenance / Bug Fixes

wordpress-tag-cloud-2This is kind of the smallest job, and least rewarding. Most people can install WordPress on their own, but there are some people, specially older generation who might find it confusing to select servers or setup their domains etc. So you can easily do those to earn some quick cash. Bug fixes sometimes can be confusing as WordPress installations can get quite complex depending on the types of customisation done the on the website. So you might need to look at what your dealing with properly before accepting this kind of job.

Theme customisation

This is purchasing/downloading a commercially or freely available WordPress theme done by someone else and customising it to fit the customer needs. This can be easy or hard depending on the level of customisation the client requires and the way that theme is developed. Sometimes themes are developed using obscure frameworks which makes it very hard to customise.

My personal advice is to avoid these unless the required customisation is quite small. These kind of websites can be a very big headache in the long run. But if this is the kind of work you like its a good starting point. You can get jobs that only includes the task of installing and setting up the theme as that can be quite confusing. Make sure that there is good documentation accompanying the theme when you accept this kind of project.

The customisation could be both front end and backend. So you might need HTML/ CSS/ Javascript and also PHP knowledge to do this kind of job. Also the ability to understand and follow instructions and patience to ready other people’s code is also helpful.

Plugin development

This is more to do with PHP development than WordPress. Plugin development is kind of advance development work, but you can charge more for this kind of work. Plugins can be developed to do all kinds of work but most of the time people looking for freelance developers to do Plugin Development knows what they are talking about, so will be easier to work with. Plugin development can be learned on your own, as there are lots of tutorials available to get you started. You need to get a good understanding of the various APIs provided by WordPress if you want to be a good plugin developer.

Custom Theme development

This was what I used to do. This is building an entire WordPress theme from scratch based on a design. Functionality of the website can be achieved by integrating plugins and theme can be developed as a WordPress child theme or totally from scratch. I used to build any required functionality into the theme it self and avoid using plugins as much as possible as can be very difficult to maintain a plethora of plugins and keep them up to date.

To develop custom themes you need to have good understanding of front end technologies, HTML, CSS, Javascript, Front end frameworks like Bootstrap, Foundation and also PHP.

From this point onwards I am going to assume you want to become a custom theme developer and continue with my post. But any other type of work is also basically same, so depending on your preference, replace custom theme development with what you prefer.

What you need to learn or know

  • Basic photoshop skills: How to open a PSD file, how to isolate layers, How to slice a PSD files. Tip: Only slice and take out images that your cant replicate with CSS. Dont use images for gradients or buttons if you can avoid it.
  • Front end: HTML, CSS, Javascript, JQuery, Front end frameworks- Bootstrap and Foundation (One of these is enough, bootstrap is more popular but I find foundation is easier to understand.)
  • WordPress: Understand how to install WordPress, how the database looks like, the file and folder structure, the dashboard, just install a test version and play around the dashboard. I suggest going to and creating a blog to get a feel of the dashboard first.
  • PHP: Basic PHP knowledge is enough to get started, you can learn as you go along.
  • MySQL: WordPress uses MySQL as the database engine, so it wil be helpful to get some understanding on how databases and tables are created, basic sql commands etc.


Obviously you wont have a portfolio when your beginning. So the best thing to do is to start building your own website using WordPress. That’s how I got my start in 2012, I built a very crappy version of my website. Which I sent to a client as a sample. My theory is something is better than nothing, so Just build something it doesn’t have to be great.

But if you have already done WordPress development in your office or during your studies. Just build a portfolio website. This can help a lot when convincing your customer about your abilities.

Where to find customers

4Best place for me to find WordPress customers is on Which is part of WordPress I think. Here its easier than on most freelance websites as you can directly talk to the customer and show off your skills. I personally find it difficult on other sites as those require complex profiles and doing tests to show your skills.

Some other places to find jobs are given below,

Do a google search for “WordPress Jobs” and you can find lots of places to find WordPress work.

How to communicate with customers

This I think is the most important part of this post. Its your communication style that will set you apart more than your work. From the initial email or message to the daily or weekly updates on the project, A client needs to be kept updated. Remember, An informed customer is a happy customer.

Follow these when your communicating with your client,

  • Be polite – Be nice and patient when dealing with the customer. Remember they might not always be technical and you might have to explain things several times to convince them.
  • Be descriptive but keep it simple – Keep the information in point form. This helps to understand and breakdown what your trying to say.
  • Give examples – Giving examples or visual aids can go along way. Use a tool like Greenshot to provide very descriptive screenshots. If your on skype share your screen or ask your customer to share their screen when talking so that you both know what your looking at.
  • Be prompt – Don’t go missing. Be quick to reply to client enquiries.

Always remember a face to face meeting is better than a call, A call is better than an email.

How to get paid

businessman holding money and clock. time is money concept ** Note: Shallow depth of field

Have a contract. I know this can be tough to do, but discuss your rates with customer and download a sample contract from the net and just send it to your client to get signed. At least provide them with a quotation. This could be helpful when trying to get paid. (I used for generating my quotations.)

And discuss how your getting paid in advance. If its overseas client, you can get paid by direct wire transfer to your bank. Which can be bit costly for the client but easier for you. To convince the client to do this, give them a discount depending on the cost they have to bare. Also you can use money transfer services like Western Union and Moneygram.

Give them an invoice, this makes you appear as a proper professional and they will take you more seriously. I use for invoicing. (You can also use if you want to quickly generate a pdf invoice. 🙂 )

And always remember to use a time tracking software to track the work that you do. This will help to make sure your charging appropriately and not overworking your self. Also this will help to keep your focus when doing work. I use Toggl to do my time tracking.

How to maintain the relationship with customers

Freelancers most of the time seems to go from project to project and client to client. But I think its important to keep a client for repeat business than trying to find new customers. Repeat business is the easiest way to earn a steady income and it will be helpful to maintain good customer relationships to do that. Here I have given some of the things I use when I deal with customers.

  • Always give more than what your getting paid for.
  • Be prompt
  • Be transparent
  • Keep them informed, at least explain what you did to them. or even better provide them with a user guide and train them on how to use the dashboard.
  • Charge them fairly.


I have tried to put down as much as possible of what I have learned down in one post. I hope this will help at least one person in getting started as a freelancer and earning money. I am planning to do a YouTube video on WordPress theme development in the future, Let me know if you would be interested in that. Thank you. 🙂

WordPress Tip – Print parent page title and if there is no parent, print current page title.

Got this requirement on one my projects to show the Parent Page Title if there is a parent page and if there is no parent page to show the current page title. To do that use the following code,

echo empty( $post->post_parent ) ? get_the_title( $post->ID ) : get_the_title( $post->post_parent );

WordPress / PHP / Magic Fields Strip unnecessary HTML Tags while leaving the ones you want

HI Its really annoying when WordPress sometimes adds tags in multiline text inputs when you really dont need them. So to get rid of those tags you can use the following php function,


Here the string part refers to the string that your getting through WordPress or maybe as a Magic fields input field. and you can use the allow part to allow any tags that you might actually want to leave in the out put.


strip_tags($postextract, '<a><ul>');

This will actually strip all HTML tags leaving anchor tags and UL tags in the output. Pretty useful hack. 🙂

Reference :

qtranslate and Magic Fields custom post menu link issue

When you add a custom post created with magic fields to a custom menu, it will not show up the language url like you want to when your using qtranslate. So how to fix it?

add this line to the qtranslate_hooks.php file in the plugin directory. Should be added at the bottom of the file. where similar hooks are there.

add_filter(‘post_type_link’, ‘qtrans_convertURL’);

Hope this helps.

How to get the upload folder in the theme files on WordPress

Ok a quick tip, How to get the upload folder in a theme file on WordPress.

Why we need this?

Well you might want to link to a pdf file or some static file from the theme file.

How to do this?

First get the upload folder into a variable,

<?php $upload_dir = wp_upload_dir(); ?>

Then use that variable in the url to get the path of the file,

<a href=”<?php echo $upload_dir[‘baseurl’]; ?>/Testfile.pdf”  target=”_blank”>Test link</a>

Simple as that.

WordPress Tip – Removing tags from post excerpt

OK a small tip that I found really useful.

When your trying to get the excerpt of the post in WordPress site you use the following method,

<?php the_excerpt(); ?>

The problem with above is that it will include its own <p> or paragraph tags. There might be an occasion where you don’t wont those tags, like I did today. So then you use the following code,

<?php echo get_the_excerpt(); ?>

Hope this helps 🙂

WordPress qtranslate plugin detect language on theme files

How to detect the language on the theme files when your using qtranslate?

Well that is a good question. Why might we want to do that? well you can display diffrent contents to users based on their selected language, That’s why… 🙂

So how to do it,

if(qtrans_getLanguage()==’th’) {
// put your the content you want to display when thai language is selected ex:
} elseif(qtrans_getLanguage()==’en’) {
// put your the content you want to display when English language is selected ex:

WordPress Weird Character issue on exported database

Hi All,
Yesterday after I uploaded one of the websites from local to staging I realized that most of the content was in some weird characters.

Ex: ’, â„¢, œ

The content was full of these.

My normal release process is,
1- do the changes on localhost
2- upload to staging server and send to client for approval
3- upon approval upload the changes to live website

so basically what I do is upload the wp-content files from file-zilla and then export the local database to a external sql file and change all the links to staging/live versions and then run the script on staging or live server.

So these weird characters was coming in the exported file of the localhost. not in the website on local host it self.

After some researching I found that although my wordpress database was in utf-8 encoding the editor I use to change the links notepad++ is in ANSI encoding.

Solution – change encoding to utf-8 on notepad++

Change encoding in notepad ++

Problem solved. 🙂

WordPress Tabs and Slides Plugin Customization

Hi Guys after a long time 🙂

I got a project that required to have a tabbed layout on its pages. This was easily achievable with numerous plugins out there. I did some research and decided to use “WordPress Tabs Slides” Plugin.

After customizing it according to the customers need I realized that the url wasnt changing to reflect the tab the user was on. The SEO company I was working was adamant about having seperate urls for each tab for SEO optimization.

So Me and my friend(Shammi) decided to customize it to show the #tag for the tab on the url. The effort was successful and I decided to share what we learned with you guys.

After installing the plugin go to the plugin directory,


and replace the tabs_slides.js file with following content,

// JoomlaWorks “Tabs & Slides” Plugin for Joomla! 1.5.x – Version 2.4
// License:
// Copyright (c) 2006 – 2008 JoomlaWorks, a Komrade LLC company.
// More info at
// Developers: Fotis Evangelou
// ***Last update: May 20th, 2008***

/* Copyright (c) 2006 Patrick Fitzgerald – Version 1.9 */
// Temporarily hide tabs
document.write(‘<style type=”text/css”>.jwts_tabber{display:none;}<\/style>’);
var pageURL = window.location.toString().slice(0, -1);
var urlArray = new Array();
urlArray = pageURL.split(‘//’)[1];
var pageId = urlArray.split(‘/’)[urlArray.split(‘/’).length-1];

var tabberOptions = {
//start on DOM ready
‘cookie’:”jwts_tc_”+pageId, /* Name to use for the cookie */
‘onLoad’: function(argsObj)
/* var t = argsObj.tabber;
var i;
if ( {
t.cookie = + t.cookie;
i = parseInt(getCookie(t.cookie));
if (isNaN(i)) { return; }
$(‘#jwts_tab ul li’).find(‘a’).each(function(index){
var str = $(this).text().toLowerCase().replace(/\b[a-z]/g, function(letter) {
return letter.toUpperCase();
str = str.replace(“Echo”, “ECHO”);
str = str.replace(“Faq”, “FAQ”);
$(this).text(str.replace(/-/gi, ” “));

var t = argsObj.tabber;
var show_type=unescape(document.location.hash.substring(1));
var i;

// alert(‘hash = ‘ + i);
if ((show_type==””) || (show_type==null))
if ( {
t.cookie = + t.cookie;
i = parseInt(getCookie(t.cookie));
if (isNaN(i)) {return;}
// $(‘#jwts_tab’).find(‘a’).text() = $(‘#jwts_tab’).find(‘a’).text().replace(“-“, ” “, flags);
//alert($(‘#jwts_tab’).find(‘a’).html().replace(“-“, ” “));

$(‘#jwts_tab’).find(‘a’).html($(‘#jwts_tab’).find(‘a’).html().replace(“-“, ” “));

//var strVal = attrib;
// alert($(‘#jwts_tab’).find(‘a’).attr(‘href’));
// alert($(‘div.jwts_tabberlive ul li’).find(‘a’).attr(‘href’));
// $(“ul.jwts_tabberlive > ul > li”).css(“border”, “3px double red”);
//alert(‘getCookie(‘ + t.cookie + ‘) = ‘ + i);
var c = argsObj.tabber.cookie;
var i = argsObj.index;
//alert(‘setCookie(‘ + c + ‘,’ + i + ‘)’);
setCookie(c, i);
var urloftab = this.tabs[i].headingText;
window.location.hash = urloftab;

// $(“#tabs > ul”).bind(“tabsshow”, function(event, ui) {
// window.location.hash =;
//var urloftab = $(‘.jwts_tabberactive’).find(‘a’).attr(‘href’);
// alert(urloftab);
//window.location.hash = urloftab;
// Cookie handling
function setCookie(name, value, expires, path, domain, secure) {
document.cookie= name + “=” + escape(value) +
((expires) ? “; expires=” + expires.toGMTString() : “”) +
((path) ? “; path=” + path : “”) +
((domain) ? “; domain=” + domain : “”) +
((secure) ? “; secure” : “”);
function getCookie(name) {
var dc = document.cookie;
var prefix = name + “=”;
var begin = dc.indexOf(“; ” + prefix);
if (begin == -1) {
begin = dc.indexOf(prefix);
if (begin != 0) return null;
} else {
begin += 2;
var end = document.cookie.indexOf(“;”, begin);
if (end == -1) {
end = dc.length;
return unescape(dc.substring(begin + prefix.length, end));
function deleteCookie(name, path, domain) {
if (getCookie(name)) {
document.cookie = name + “=” +
((path) ? “; path=” + path : “”) +
((domain) ? “; domain=” + domain : “”) +
“; expires=Thu, 01-Jan-70 00:00:01 GMT”;
// The tabs!
function tabberObj(argsObj)
var arg;









for(arg in argsObj){this[arg]=argsObj[arg];}
this.REclassMain=new RegExp(‘\\b’+this.classMain+’\\b’,’gi’);

this.REclassMainLive=new RegExp(‘\\b’+this.classMainLive+’\\b’,’gi’);

this.REclassTab=new RegExp(‘\\b’+this.classTab+’\\b’,’gi’);

this.REclassTabDefault=new RegExp(‘\\b’+this.classTabDefault+’\\b’,’gi’);

this.REclassTabHide=new RegExp(‘\\b’+this.classTabHide+’\\b’,’gi’);

this.tabs=new Array();if(this.div){this.init(this.div);this.div=null;}}

if(!document.getElementsByTagName){return false;}
this.tabs.length=0;childNodes=e.childNodes;for(i=0;i<childNodes.length;i++){if(childNodes[i].className&&childNodes[i].className.match(this.REclassTab)){t=new Object();t.div=childNodes[i];this.tabs[this.tabs.length]=t;





if(this.titleElementsStripHTML){t.headingText.replace(/<br>/gi,” “);t.headingText=t.headingText.replace(/<[^>]+>/g,””);}








this.tabShow(defaultTab);if(typeof this.onLoad==’function’){this.onLoad({tabber:this});}
return this;};

rVal,a,self,tabberIndex,onClickArgs;a=this;if(!a.tabber){return false;}

if(typeof self.onClick==’function’){onClickArgs={‘tabber’:self,’index’:tabberIndex,’event’:event};

rVal=self.onClick(onClickArgs);if(rVal===false){return false;}}
self.tabShow(tabberIndex);return false;};tabberObj.prototype.tabHideAll=function()
{var i;for(i=0;i<this.tabs.length;i++){this.tabHide(i);}};

{var div;if(!this.tabs[tabberIndex]){return false;}
div=this.tabs[tabberIndex].div;if(!div.className.match(this.REclassTabHide)){div.className+=’ ‘+this.classTabHide;}
this.navClearActive(tabberIndex);return this;};

tabberObj.prototype.tabShowByName=function(tabberName){var i;for(i=0;i<this.tabs.length;i++){if(this.tabs[i].headingText==tabberName){this.tabShow(i);break;}}return this;};

{var div;if(!this.tabs[tabberIndex]){return false;}


this.navSetActive(tabberIndex);if(typeof this.onTabDisplay==’function’){this.onTabDisplay({‘tabber’:this,’index’:tabberIndex});}
return this;};

// var urloftab = this.tabs[tabberIndex].headingText;
// alert(urloftab);
// window.location.hash = urloftab;
return this;};

{this.tabs[tabberIndex].li.className=”;return this;};function tabberAutomatic(tabberArgs)
tempObj=new tabberObj(tabberArgs);divs=document.getElementsByTagName(“div”);for(i=0;i<divs.length;i++){if(divs[i].className&&divs[i].className.match(tempObj.REclassMain)){tabberArgs.div=divs[i];divs[i].tabber=new tabberObj(tabberArgs);}}
return this;}
function tabberAutomaticOnLoad(tabberArgs)
{var oldOnLoad;if(!tabberArgs){tabberArgs={};}
oldOnLoad=window.onload;if(typeof window.onload!=’function’){window.onload=function(){tabberAutomatic(tabberArgs);};}else{window.onload=function(){oldOnLoad();tabberAutomatic(tabberArgs);};}}
if(typeof tabberOptions==’undefined’){tabberAutomaticOnLoad();}else{if(!tabberOptions[‘manualStartup’]){tabberAutomaticOnLoad(tabberOptions);}}

/* WordPress Slides */

wtsslide = function(wrapper,speed, obj){

//jQuery(obj).text(jQuery(obj).text().replace(‘.’, ”));
if (jQuery(obj).text().indexOf(“(Hide)”) >= 0) {
jQuery(obj).text(jQuery(obj).text().replace(‘(Hide)’, ”));
return true;

jQuery(obj).text(jQuery(obj).text() + ‘ (Hide)’);


wtsaccordion = function(group,wrapper,speed){

Please pay attention to the Bolded code, those are what we added. Hope this is helpful feel free to contact me if you have any questions.