Formats an internal or external URL link as an HTML anchor tag.

Formats an internal or external URL link as an HTML anchor tag.

This function correctly handles aliased paths, and adds an 'active' class attribute to links that point to the current page (for theming), so all internal links output by modules should be generated by this function if possible.

However, for links enclosed in translatable text you should use t() and embed the HTML anchor tag directly in the translated string. For example:

t('Visit the <a href="@url">settings</a> page', array(
  '@url' => url('admin'),

This keeps the context of the link title ('settings' in the example) for translators.


rszrama’s picture

It is not necessary to wrap the $text argument with t() as long as the html option is FALSE (the default). l() will automatically check_plain() the link text for you.

zzolo’s picture

t() is necessary if you are providing translatable text. The purpose of t() is to provide translation, while check_plain() is used to escape any possible malicious character. Often t() is used an easy way to provide secure text since it pushes things through check_plain, but this is just an added bonus. use t() for translating!.

chriscohen’s picture

This will produce a link with the class="widelink" and rel="lightbox" attributes on the a tag.

  t('My link'),
    'attributes' => array(
      'class' => 'widelink',
      'rel' => 'lightbox',
chrsim79’s picture

All components are already mentioned but no example straight into you face, so to open link in new windows use:

  l(t('Title'),$url, array('attributes' => array('target' => '_blank')));

Hope this will save time for some of you.

rimian’s picture

That's helpful but the HTML won't validate for strict doc types.

Something like the following code should work:

  l(t('Title'), $url, array('attributes' => array('onclick' => 'window.open(this.href); return false;')));
alexanderpas’s picture

To create a link to a named anchor (e.g. #namedanchor), you will need to use a small work-around.

  l('linktext', '', array('fragment' => 'namedanchor', 'external' => TRUE));

To create a hash-only link (to #), you'll need to adapt it to:

  l('linktext', '', array('fragment' => ' ', 'external' => TRUE));

(Note that the fragment does contain a space.)

vadbars@drupal.org’s picture

This does not work for me (D6). When I am writing this:
print l(t('Functions'), '', array('fragment' => 'myfunctions', 'external' => TRUE));

Then get:
<a href="/%2523myfunctions">Functions</a>

When I am writing this:
print l(t('Functions'), '', array('attributes' => array('fragment' => 'myfunctions', 'external' => TRUE)));

Then get this code:
<a href="/" fragment="myfunctions" external="1">Functions</a>

How do I remove %2325 instead of #?

bsenftner’s picture

In porting a D5 site to D6, I found within their Ubercart logic they are generating a purchase item image-link like this:

   $buy_img = theme_image('sites/all/themes/customTheme/images/buy.png', 'BUY', 'Buy The' . check_plain($node->title), array('class' => 'coins'));
    $buy_link = 'cart/add/e-p' . $node->nid . '_q1_m0?destination=cart/checkout';
    print l( $buy_img, $buy_link, array('html' => true) );

The problem is the l() function is supposed to generate this:


but is generating this incorrect link with character encoding rather than '?' and '=' characters:


Apparently, the D5 l() function does not do that same character encoding, and was not an issue... So here's the fix for D6:

    $buy_link = 'cart/add/e-p' . $node->nid . '_q1_m0';
    print l( $buy_img, $buy_link, array('html' => true, 'query' => 'destination=cart/checkout') );

The solution being to place the query string portion of the link into a 'query' attribute.

zazinteractive’s picture

Very useful comment. Thanks

cbearhoney’s picture

I thought this was helpful...

if (user_access('create blog entries')) {
    $items[] = l(t('Create new blog entry.'), "node/add/blog");

Used in blog module function blog_page_last

whoisrich’s picture

To clarify for those who want their module to support the 'Clean URLs' option.

  $var = l("Click Text", "ModuleName");

returns a link complete with <a href= tags.

For a link that goes with your hook_menu() and 'page arguments' you just add the arguments to the path.

  $var = l("Click Text", "ModuleName/value/value");

If you need a link without the <a href= tags use 'url' instead.

  $var = url("ModuleName/value/value")
dotpex’s picture

Various implementation of l() function


  l(t('Link text'), 'about-us');

with class:

  l(t('Link text'), 'about-us', array('attributes' => array('class' => 'about-link')));

link to user:

  l(t($user->name), 'user/'.$user->uid, array('attributes' => array('class' => 'link', 'id' => 'xxx', 'title' => 'Title')));

Always use user/user id!!!

simple link with image:

 $img = '';
 l($img, 'user/3', array('html' => array('html' => 'true')));
 // also valid
 l($img, 'user/3', array('html' => 'true'));

complex link with image:

 l($img, 'user/3', array('attributes' => array('class' => 'link', 'id' => 'xxx', 'title' => 'Title'), 'html' => 'true'));
zeropaper’s picture

*might* lead to security issues.. (I mean... sure I did some of those mistakes... back in the days... :) )

the l() function sanitize the first argument (the text) when the option "html" isn't TRUE...
it means that you need to really take care about what the first argument will be if using the "html" option

For example:

// making an img tag like might not be the safest solution
//$img = '';
// this would be much better, but still, you need to know that the $directory variable is also safe
$img = theme('image', $directory .'/images/rssIcon.png');
// this is, I think, not possible...
//l($img, 'user/3', array('html' => array('html' => 'true')));
// is ok
l($img, 'user/3', array('html' => 'true'));
// well, stop me if I am wrong, but I guess might be useful to wrap $user->name within t()..
//l(t($user->name), 'user/'.$user->uid, array('attributes' => array('class' => 'link', 'id' => 'xxx', 'title' => 'Title')));
// Something who might be handy if you need to "define" an HTML id is the form_clean_id()
// it will prevent to have twice the same id in the HTML (which is bad :) )
l($user->name, 'user/'.$user->uid, array('attributes' => array('class' => 'link', 'id' => form_clean_id('xxx'), 'title' => 'Title')));

Just a note..
if you need to have a variable in a translation.. like it might have been the case in the example

l(t('Some string related to @user_name', array(
'@user_name' => $user->name
)), 'user/'. $user->uid);
// I might explain why it might be better to use @user_name over a simple @user... but it's not the topic ;)


theme('username', $user);
// whatever.. 

anyway, it was nice from you to help!

ndvo’s picture

I don't think it is a good idea to use t($user->name)

This may create a translatable string for each user name. The translator will never reach a hundred per cent translation unless he translates each username, and if he does so he will change a value he shouldn't change.

I'd suggest to use t() every time a string needs to be translated, and in no other circumstance.

sobi3ch’s picture

in $options you forget to explain language options, to protect links against language prefix names like 'site.name.com/en/sites/defailt/files/blag.pdf'

I protect my site against this using below solution

$link = l($title, $path, array('language' => 'pl'));
nirmal_george’s picture

l($img, 'user/3', array('attributes' => array('class' => 'link', 'id' => 'xxx', 'title' => 'Title'), 'html' => 'true'));
bloom’s picture

If you look at the description of the function in Drupal 6 parameters you will find under $options:

Additional $options elements used by the url() function.

But I was not able to put additional parameters into URL in this case:

print l("Click here", $internal_path, array(
   'listing_url' => 'data'

So I made the following changes as the user described above and it worked:

print l("Click here", $internal_path, array(
   'query' => array(
      'listing_url' => 'data'
NancyDru’s picture

Actually all the "attributes" values are sanitized in drupal_attributes, so if you do anything to sanitize it yourself, you can end up double encoding. For example, l($title, $path, array('attributes' => array('title' => t('All about @names', array('@names' => 'Adam & Eve'))))) will result in the ampersand being double encoded. In this example, it is okay to use '!names' because drupal_attributes will do a check_plain for you.

8837’s picture

to create a hash-only link (to #) you'll need to adapt it to:
l('linktext', '', array('fragment' => ' ', 'external' => TRUE));
(note that fragment does contains a space.)

Thanks to alexanderpas!

yngens’s picture

Can't figure out how to combine attributes and fragment, since I need both - the first one to apply a class, second one to generate proper URI to the comment. What is wrong in my code below?

$block_content .= l(t('Conversate'), "node/$links->nid", array('attributes'=>array('class'=>'comment')), array('fragment' => "comment-$links->cid")).'';
Zatox’s picture

I rewrote your code with the correct syntax (I think but have not tested)
Main problem was: 'fragment' should not be in another array but just a key of the $options array.

$block_content .= l(t('Conversate'), "node/".$links->nid, array('attributes'=>array('class'=>'comment'), 'fragment' => "comment-".$links->cid).'';

If this code doesn't work, here is my code wich, I'm sure works. Hope it will help you

 print l("View responses", "node/".$fields['nid']->content, 
					'fragment' => 'responses', 
					'attributes' => array (
								'class' => 'btn_arrow_blue'
					'html' => TRUE


Pretty much the same: a "class" attribute, an anchor and some html in the label (that's why I need the 'html' => TRUE

It builds:

<a class="btn_arrow_blue" href="/node'spath#responses">
<span>View responses</span>
Manuel Garcia’s picture

If you want to use the l() function and provide the destination:

l('Login first','user/login', array(
    'query' => array(
      'destination' => 'node/'. $node->nid

The above code will provide you with an anchor tag with the href pointed to user/login?destination=node/NID

This is only useful if you are linking to a page that consist of a form, and you want the user to be redirected to another page after submitting the form.

For all other options available, make sure to see the documentation for the url() function.

tmsimont’s picture

You can use the destination of the current page with drupal_get_destination() like so

    'query' => drupal_get_destination()
HydroZ’s picture

If you want to use a link like an event-handler, your code could look like this:

  $link =  l("Click me", $_GET['q'],  array("attributes" => array("title" => t("Click this link"), "onclick" => '$("div.element_with_onclick_event").trigger("click");'), 'fragment' => 'anything'));

This will create an HTML-Markup like this:

<a class="active" onclick="$(&quot;div.element_with_onclick_event&quot;).trigger(&quot;click&quot;);" title="Click this link " href="/current_path#anything">Click me</a>

When you click on this link, the Trigger-Event "Click" for div.element_with_onclick_event will be called...

yub_yub’s picture

I thought it might help some people to say that sometimes you need to add print, like so:

   print l(t('Link text'), 'node/10'); 
anthony0perez’s picture

I was wondering what you would do in the case of having a image and a node id with drupal_get_destination()?

Would it be like this?

$nid_img_link = l(
'html' => TRUE,
'query' => drupal_get_destination()

bobmct’s picture

I've been trying to get the use of the l() function in version 6.25 down without success. What I am trying to create for output is this:


and my code looks like this:

l('Instructor', $p, array('html' => true, 'query' => array( 'a' => "$a", 'b' => "$b", 'c' => '0')));

which results in the first parameter being correct but the remaining are still encoded such as:

Scouring these docs and just about everything else I could find online I cannot figure out the correct format for the l() function to get this to work as needed. Would someone please explain the correct function coding to achieve the desired results?


zombree’s picture

When I ran into this problem, the solution I found was to use url(). It will create the query parameters without encoding them.

For more info see this comment on the url() documentation page.

mc0e’s picture

(presumably too late for bobmct, but someone else will want this)

The $options array is passed on to url(), so you can add GET paramters to the URL like so:

    print l("text","link/path", array("query" => "a=b&c=d"));

You still need to construct the GET parameter string, but it's closer to what you wanted.

no2e’s picture

There are some examples for links including an <img> as anchor text:

drudrew’s picture

When you use a link or file name that has an ampersand (&), url() or l() will double-encode the ampersand to %2526. It is because of drupal_urlencode() trying to compensate for Apache's mod_rewrite's behavior. I see good intention but it's not the ideal place to accomplish this. I believe it is not the case for D7 or 8. See drupal_urlencode() for the few other cases that are double-encoded.

It is especially problematic when you use url() or l() to create a link to a static file that has an ampersand in the file name.

priyankaarai’s picture

I have added this code to overwrite incomplete fields in content complete block.

$link = l(t($complete_data['nextfield']), 'node/'. $complete_data['nid'] .'/edit/'._multistep_get_field_step($complete_data['nextname'],$complete_data['type']), array(
'query' => array('fieldname'=> $complete_data['nextname'], 'destination' => drupal_get_path_alias('homebox/1'))

problem here is its only settings destination not 'fieldname'.

luthien’s picture

I have the following "custom" code for file downloads in ubercart. I'm sending the direct link to download the file, no protection is needed:

$my-url = 'www.mydomain.com/downloads/' . $user_file->filename;
$output .= l($my-url, $my-url, array('absolute' => TRUE)) ."\n";

The user gets the email with the following:

Download: www.mydomain.com/downloads/file-name-here (but the url links to https://www.mydomain.com/downloads/file-name-here and I only need the url without https://).

If I change the code to use http://www... the url links to https:// instead of the specified url.

any ideas what to pass to the array to get the desired result? thanks.

hafizu97’s picture

but how do I print l(); function into form.inc ?