Mathias Bynens

Optimizing the asynchronous Google Analytics snippet

Google has been beta-testing their asynchronous Analytics snippet for a while now. The code has been revised several times already, but still I think it can use some more optimizations.

The latest snippet is the following:

<script>
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXX-X']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
</script>

How it works

In case you’re wondering what’s with the _gaq variable, this is described in the Google Analytics Asynchronous Tracking Guide™:

The _gaq object is what makes the asynchronous syntax possible. It acts as a queue, which is a first-in, first-out data structure that collects API calls until ga.js is ready to execute them. To add something to the queue, use the _gaq.push method.

To be more precise — initially, _gaq is just a plain JavaScript Array. The snippet then uses the native push method to add some entries to the end of the array, such as specifying your account ID. After ga.js is loaded from Google’s servers, the _gaq.push method is replaced with a custom method, which executes all entries in the _gaq array. At this point, _gaq is no longer an Array, but an [Analytics] Object, and instant execution of the tracking methods is possible.

The next part of the snippet — (function() { … })(); — injects a new script element in the document to load ga.js, where all the magic happens. This bit of code is wrapped inside a self-invoking anonymous function, to avoid polluting the global scope with the temporary variables created in the process.

Possible optimizations

Don’t use _gaq || []

The first line of the script reads:

var _gaq = _gaq || [];

This line is there to allow multiple Analytics snippets in the same document. It ensures that the second snippet doesn’t overwrite a _gaq defined by the first. In other words, if _gaq is already defined, the script will continue to use that variable; if not, it creates a new array instead. However, there are far better techniques to instantiate multiple trackers than just including the same snippet twice, with a different account ID.

So, unless you’re using multiple Analytics snippets in the same document using bad code, you can safely use this instead:

var _gaq = [];

One push should suffice

The example snippet adds two elements to the _gaq array, by using the push method twice.

_gaq.push(['_setAccount', 'UA-XXXXX-X']);
_gaq.push(['_trackPageview']);

While the above is probably a bit more readable, this can perfectly be written as a single push statement:

_gaq.push(['_setAccount', 'UA-XXXXX-X'], ['_trackPageview']);

Of course, if you’re using more advanced tracking settings, you’ll need to push even more commands. You can safely do this for all of them at once.

Combined with the previous optimization, we don’t even need .push anymore:

var _gaq = [['_setAccount', 'UA-XXXXX-X'], ['_trackPageview']];

Don’t set ga.type

Before inserting the dynamically created script element into the DOM, its type attribute is set to text/javascript:

var ga = document.createElement('script');
ga.type = 'text/javascript';

This is entirely unnecessary. In every single A-grade browser, script elements default to text/javascript when the type attribute is omitted. Moreover, in HTML5, the type attribute on script elements is optional. It’s safe to omit this line.

Declare all variables in one go

The snippet uses two variables inside the anonymous function: ga, which holds the dynamically generated script element; and s, which is a reference to the first script element to occur in the document.

var ga = document.createElement('script');
// …
var s = document.getElementsByTagName('script')[0];

By declaring both vars in one go and by renaming ga into g, we can get rid of some bytes:

var g = document.createElement('script'),
s = document.getElementsByTagName('script')[0];

Create shortcut references for recurring vars

As you can see, document is used twice. By passing document as an argument to the anonymous function, we can squeeze out even more bytes:

(function(d) {
var g = d.createElement('script'),
s = d.getElementsByTagName('script')[0];
// …
})(document);

We can go even further by also passing "script" as an argument, since that string is used twice as well:

(function(d, t) {
var g = d.createElement(t),
s = d.getElementsByTagName(t)[0];
// …
})(document, 'script');

To comply with the “require parens around immediate invocations” option in JSLint, you’ll have to use (function() { }()); instead of (function() { })();.

(function(d, t) {
var g = d.createElement(t),
s = d.getElementsByTagName(t)[0];
// …
}(document, 'script'));

Use the protocol check only when it’s needed

The ga.js file needs to be served via the same protocol as the document it’s used in. To make sure of this, the default Analytics snippet includes a simple protocol check:

ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';

Most sites run over HTTP (and HTTP only), not HTTPS/SSL. In these cases, the SSL check isn’t needed at all, and you can safely remove it.

Aside from saving a few bytes, I found that omitting the protocol check and replacing it with a hardcoded string is up to two thousand times faster. If you’re sure your site can’t or won’t be used over SSL, there’s no reason not to change this.

ga.src = 'http://www.google-analytics.com/ga.js';

Heck, we can even remove the http: scheme so we end up with a protocol-relative URL:

ga.src = '//www.google-analytics.com/ga.js';

I really wonder why Google doesn’t store the different ga.js files under the same path, i.e. make http://www.google-analytics.com/ga.js serve up the HTTP version and https://www.google-analytics.com/ga.js the SSL version. That way, we could use this technique and omit the ternary protocol check in all cases, which would greatly simplify things.

Final result

<script>
var _gaq = [['_setAccount', 'UA-XXXXX-X'], ['_trackPageview']];
(function(d, t) {
var g = d.createElement(t),
s = d.getElementsByTagName(t)[0];
g.async = true;
g.src = '//www.google-analytics.com/ga.js';
s.parentNode.insertBefore(g, s);
}(document, 'script'));
</script>

We have reduced the original snippet of 440 bytes to one of 293 bytes with better performance. Finally, we can minify this, by removing unnecessary whitespace and semicolons, and replacing true in g.async = true with 1:

<script>var _gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];(function(d,t){
var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.async=1;g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)
}(document,'script'))</script>

The optimized code minifies to 249 bytes and executes faster than the original (440 bytes). (Note that I added some line breaks in the above code block to prevent you from having to scroll to read it all. I didn’t count these line breaks as extra characters / bytes.)

How to use it?

Google recommends placing the asynchronous snippet as high as possible in the document:

One of the main advantages of the asynchronous snippet is that you can position it at the top of the HTML document. This increases the likelihood that the tracking beacon will be sent before the user leaves the page. We’ve determined that on most pages, the optimal location for the asynchronous snippet is at the bottom of the <head> section, just before the closing </head> tag.

However, you can place the snippet in the <body> as well, be it at the top, or at the bottom. If you’re already using separate .js files for other scripts, you can even include the snippet in there so it will be cached along with the other scripts. This can be a valid reason not to put the code in the <head> after all.

Whatever position you choose, just make sure to insert the snippet before all other scripts — if not, the tracking will not begin until the other scripts have been loaded, and you lose the big advantage of asynchronous tracking (i.e. starting the tracking process before the document is finished loading).

Comments

Brian wrote on :

Google has different HTTP and HTTPS subdomains (‘www’ and ‘ssl’ respectively) for performance reasons. Using ‘www.’ for HTTPS requests is going to be slower and it may not even work in some geographic areas.

Mathias wrote on :

Brian: Interesting. Can you provide any links to documents backing that up? It would seem the name of a subdomain shouldn’t matter on a properly configured web server, SSL or not.

Nate Cavanaugh wrote on :

Most ‘micro-optimizations’ like this end up consuming more time than the benefit they provide. When you gzip the page, what’s the delta of the changes before and after? While not everyone gzips their page, if they’re worried about 200 bytes, they are either already gzipping, or should be.

Usually for space saving stuff like this, I try to use the machine as much as possible for optimizations, and keep the original code as readable as possible.

Overall though, I think this is a cool look at how the new async Google Analytics code works (especially the object replacement which is quite clever).

Do you have any total before and after on the speed tests?

Thanks for the writeup :)

Mathias wrote on :

Nate: I created three HTML5 documents: one with the original asynchronous Analytics snippet, one with the optimized but non-minified snippet, and another with the optimized and minified snippet. All files are served with gzip compression enabled.

Here are the file sizes of the different HTML5 documents, before and after compression:

  • With the original snippet: 735 bytes, after gzip 432 bytes
  • With the optimized (but non-minified) snippet: 614 bytes, after gzip 389 bytes
  • With the optimized and minified snippet: 569 bytes, after gzip 380 bytes

As you can see, there’s an improvement of ± 23% in byte size when using the optimized snippet without gzip. When using gzip, the compression rate is still around ± 12%.

Note that gzip works by storing common blocks of data and compressing those, so the results may vary depending on the rest of your HTML document. This is also the reason why I created HTML files instead of .js files and testing those sizes before and after compression.

I agree this is a matter of micro-optimization, but it’s important to look further than just byte size. An equally important improvement of the optimized snippet is the increased performance.

Kyle Simpson wrote on :

One thing to note about this new version of GA async is that they’ve lost the robustness against the <base> tag problem in IE6. The problem basically is that you have to insert a script tag before (in the DOM) the <base> tag appears, or you will crash IE6. It has to do with IE6 not recognizing the close of the <base> tag and behaving incorrectly as a result.

The solution (albeit less graceful) is to always insert scripts as the first child of the <head>, ensuring it comes before the <base> tag if one appears. The way you do that is get a reference to the head element (instead of a script), and insertBefore() on its firstChild. You have to do an additional check to make sure the object reference is valid, so the extra code is probably why Google abandoned it.

But I do think this snippet should come with the caveat that you cannot safely use it with document that has a <base> tag in it.

Mathias wrote on :

Kyle: Good point; I hadn’t thought of that. In my opinion, an even better solution is to explicitly ‘close’ the base tag inside a conditional comment for IE 6 only.

<base href="http://example.com/foo/bar/"><!--[if lte IE 6]></base><![endif]-->

You pretty much have to do this anyway — otherwise IE6 considers all following elements to be descendants of the <base> element instead of <head>. Obviously, this can cause severe performance/bandwidth issues.

j@ubourg wrote on :

Sadly, the async property set as true or 1 will not work (see jQuery.ajax() async versus HTML5 <script async>). You’d better go with something like this:

var _gaq = [];
_gaq.push(['_setAccount', 'UA-XXXXX-X'], ['_trackPageview']);
(function(d, t, a) {
var g = d.createElement(t),
s = d.getElementsByTagName(t)[0];
g[a] = a;
g.src = '//www.google-analytics.com/ga.js';
s.parentNode.insertBefore(g, s);
})(document, 'script', 'async');

Mathias wrote on :

j@ubourg: I don’t think I understand what you mean. In which browser does script.async = true fail? (Other than Opera, which doesn’t currently support the async and defer attributes at all.)

j@ubourg wrote on :

Mathias: Firefox, and I learned it the hard way (as you can see in the jQuery dev thread I linked to). Experiments clearly showed a script tag with async = 1 was acting synchronously whereas a script tag with async = "async" was acting asynchronously (tested with PHP scripts using delays to track the chain of script tag execution and how they were scheduled by browsers). Hope this helps.

Mathias wrote on :

j@ubourg: Interesting. Which browsers and browser versions did you test? Are your tests available online?

After running the Analytics snippet in the console and checking the generated HTML source using Firebug, it seems like everything is in order. The following code is inserted dynamically:

<script async="" src="//www.google-analytics.com/ga.js">

Which is exactly what we’re looking for — in HTML, async="" is the same as async="async" or just async, for that matter. See boolean attributes in HTML5.

Are you saying all browsers implement this incorrectly?

var foo = document.createElement('script');
foo.async = true; // or foo.async = 1;

j@ubourg wrote on :

Mathias: I would have to dig my tests up. I’d say it was a couple Firefox subversions ago. Don’t trust the generated code: make a PHP script with a delay param that’ll wait for X seconds, put a couple script tag injections in your page (with the PHP script with different delay parameter values as src) and see how it behaves. I guarantee you’ll be surprised ;)

My guess is Firefox only acts asynchronously when async = "async" but it may have been fixed since my last tests (I can’t find my old tests right now).

Browsers behaviors are quite inconsistent in the script loading department. Some will block on injected script tags, others will always act asynchronously with them. Firefox is the most consistent, provided you set the async property properly (again, to be tested with latest, they could have fixed the issue, I haven’t explored this in a while).

If you wanna see how much ‘fun’ I used to have with script tag injection, I suggest you check this thread. Not related to the issue at hand but gives a good view at how crazy some browser’s implementations can be and how crazy you can go with them.

Rob Flaherty wrote on :

Great post! After reading this I took a shot at optimizing the asynchronous tracking code from the analytics service Clicky. But Clicky’s async snippet is modeled after GA’s, so the code ended up being more or less a direct copy of yours. =)

Original code (raw):

<script>
var clicky = { log: function() { return; }, goal: function() { return; }};
var clicky_site_id = XXXXXX;
(function() {
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = (document.location.protocol == 'https:' ? 'https://static.getclicky.com' : 'http://static.getclicky.com') + '/js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(s);
})();
</script>

<a title="Web Statistics" href="http://getclicky.com/162739"></a>
<noscript><p><img alt="Clicky" width="1" height="1" src="http://in.getclicky.com/XXXXXXns.gif" /></p></noscript>

Optimized version (gist):

<script>
var clicky = { log: function() { return; }, goal: function() { return; }},
clicky_site_id = XXXXXX;
(function(d, t) {
var c = d.createElement(t),
s = d.getElementsByTagName(t)[0];
c.async = 1;
c.src = '//static.getclicky.com/js';
s.parentNode.insertBefore(c, s);
})(document, 'script');
</script>

<!-- This supports tracking for users with JavaScript disabled. This should go in <body> -->
<noscript><img alt="" width="1" height="1" src="http://in.getclicky.com/XXXXXXns.gif" /></noscript>

Mathias wrote on :

Nice work, Rob! You can take it even further if you want, by optimizing these lines:

var clicky = { log: function() { return; }, goal: function() { return; } },
clicky_site_id = XXXXXX;

You don’t really need the return, an empty function has the same effect. To create an empty function it’s enough to initialize a new Function object.

var clicky = { log: Function(), goal: Function() };

Also, since the function is used twice, you can store its return value in a variable to improve efficiency (and save some more bytes in the process).

var f = Function(),
clicky = { log: f, goal: f };

The full code can be viewed in my fork of your gist.

Rob Flaherty wrote on :

Very nice! I knew there had to be a way to improve those empty functions…

BTW, I’ve been testing out the snippet on my blog today and it indeed works.

Sander Aarts wrote on :

Nice article Mathias. You pass in document and 'script' as parameters to the self-invoking anonymous function. Passing document may have a very slight performance gain (haven’t tested it) as it creates a reference to the document in the function’s local scope, without the need for the script to run up the scope chain. But what’s the gain of passing 'script', instead of initializing it within the functionality body? That would make it easier to read in my opinion and it would even 1 byte smaller :) when combined with the other declarations.

Mathias wrote on :

Sander: The only reason I’m passing 'script' is to save some bytes. I haven’t tested the performance gain — I doubt there is any. How would declaring the variable containing 'script' inside the functionality body make it any smaller though? I made a comparison of both snippets, and they’re both exactly the same size when minified. Before minifying (and using proper code indenting) your version would be even larger (by a few bytes). Am I missing something here?

Antti Kokkonen wrote on :

Thanks a lot for this. Poetrically beautiful optimization! and more importantly: works perfectly :)

And like that is not enough, the flow of optimization was very well presented, and will help me optimize other snippets as well.

Martin wrote on :

Nice article! I’ll try it out.

Do you know same snipped to fix document.write on synchronously loaded scripts ?

I was trying this, but I have problems on Chrome:

document.write = function(t) {
var b = (document.getElementsByTagName('body')[0] || document.documentElement);
b.innerHTML = b.innerHTML + t;
};

Thanks in advance!

Mathias wrote on :

Martin: You really shouldn’t be using document.write hacks when there’s an alternative available.

In your code sample, you are referencing a variable t, but it never gets a value as far as I can see. Also, why use document.getElementsByTagName('body')[0] instead of just document.body? Please note that resetting the innerHTML of the entire body is evil and shouldn’t be done ever.

Martin wrote on :

Mathias: Thanks for your reply. I’m not using document.write, but I’m trying to resolve ad tags loading, like AdSense scripts. Those scripts are using document.write and it is impossible to load them async. For that reason I need to overwrite document.write.

Wim Leers wrote on :

Aside from saving a few bytes, I found that omitting the protocol check and replacing it with a hardcoded string is up to two thousand times faster.

Two thousand times faster is a lot. In Google Chrome 5, on my hardware, it’s “only” between 200 and 300 times faster. I don’t think this relative speed-up is very relevant: what’s 1 ms versus 0.05 ms? We don’t care about a single ms. You should’ve included the absolute speedup. For me, the hardcoded string only needs 0.8 ms, the ternary protocol check needs a whopping 180 ms. And this 180 ms is very relevant, since it becomes very noticeable.

I really wonder why Google doesn’t store the different ga.js files under the same path, i.e. make http://www.google-analytics.com/ga.js serve up the HTTP version and https://www.google-analytics.com/ga.js the SSL version.

You’re absolutely right in questioning this. It would allow for more elegant GA initialization code, as you already said — by using a protocol-relative URL and thereby obsoleting the document’s protocol check. However, this is probably a matter of optimizing the download speed of ga.js: it’s easier to manage and optimize HTTPS and HTTP servers separately (by running them on different domains). I can’t give you details because I’m not very knowledgeable in that area, but I do know that this is true, at least today, and for the typical server set-up. Although I agree that Google should focus on this as the next step of improving GA performance — they’ve got enough brain power to make it fast. 180 ms for the default ternary check in one of the fastest browsers currently in existence, on very modern hardware indicates that it’s likely many multiple times slower on older computers with slower browsers. And thus an excellent additional performance boost.

The shorter variable names and passing of variables via the anonymous function’s parameters to save bytes are completely overkill in my opinion. They reduce legibility to gain mere bytes.

Overall, very nice article. I’m definitely going to use this optimization on my websites! :)

Mathias wrote on :

Wim:

Two thousand times faster is a lot. In Google Chrome 5, on my hardware, it’s “only” between 200 and 300 times faster.

Yeah, same here in Chrome. The bigger difference occurred in Firefox (3.6.3 at the time), and since this was the most impressive result I had seen, that’s the one I referred to. Of course, YMMV :)

I don’t think this relative speed-up is very relevant: what’s 1 ms versus 0.05 ms? We don’t care about a single ms. You should’ve included the absolute speedup. For me, the hardcoded string only needs 0.8 ms, the ternary protocol check needs a whopping 180 ms. And this 180 ms is very relevant, since it becomes very noticeable.

You should note that the test case measures the speed for 100,000 iterations of that single line of code — not one iteration. Feel free to view the source for more information.

As you mentioned before, the results of the test case do not only depend on the browser, but also on the hardware and CPU usage. These client-side variables are impossible to control.

That’s why I figured I’d just go with a relative measure instead. If the Mac version of Chrome 5 executes a certain statement 300 times faster than another, it’s safe to assume this will be the case for any other Mac configuration with the same browser/version, even though the absolute numbers may differ based on processor/CPU usage etc.

But again, it was just an example. If you want to collect raw data on different computer/browser/OS combinations, feel free to use the test case — that’s what it’s there for!

The shorter variable names and passing of variables via the anonymous function’s parameters to save bytes are completely overkill in my opinion. They reduce legibility to gain mere bytes.

It seems legibility doesn’t really matter for a snippet that is blindly copy-pasted all over the Internet. Most people using it don’t seem to realize what exactly the script does — else, someone else would have written this article much earlier. The protocol check is the perfect example of this. Of course, I don’t have the exact numbers, but I bet that over 90% of all websites with that check in their code don’t need it. Let’s face it, most sites are HTTP-only and don’t even work over HTTPS/SSL. I can see why Google would include it in their default snippet though.

Having said that, the optimizations you’re talking about do not only save bytes, but also offer a small performance gain. Passing document as a variable to the function creates a reference to document in the function’s local scope. The advantage of this is that every time document is used from inside the function (three times in the original snippet), the script doesn’t need to run up the scope chain to look it up.

Wim Leers wrote on :

You should note that the test case measures the speed for 100,000 iterations of that single line of code — not one iteration. Feel free to view the source for more information.

Hah! I can’t believe I didn't notice that. You’re absolutely right to mention the relative difference then :)

It seems legibility doesn’t really matter for a snippet that is blindly copy-pasted all over the Internet.

I thought about this too, but I disagree softly. While it’s true what you say (about blindly copying and about the small performance gain), I think it increases the threshold to understanding unnecessarily.

Although I can definitely agree with your point-of-view too: it’s just a matter of taste — I prefer legibility over minor performance gains. But yes, it does make perfect sense to go for every performance gain possible when your code is deployed on millions of websites.

Clearly, even the only minor criticisms I could formulate are easily refutable. Which means this is very well written.

Thanks for taking the time to answer! :)

Kyle Simpson wrote on :

On the topic of why Google uses SSL on one domain and non-SSL on another domain... I think it may be more performant to do so because the web server needs to spin up the SSL Engine for a single virtual-host, meaning (I bet) that engine is spun up (or at least accessed I'm sure) for every request to that comes into a SSL enabled virutal-host. This would mean all non-SSL traffic would be slightly slower for the sake of that SSL engine start or access. Just a thought.

I agree it's annoying not to be able to use the :// hack... but perhaps Google believes there's a greater performance gain elsewhere that makes up for the few bytes and the ternary. Not sure.

Mathias wrote on :

Kyle: Brian just tweeted that using //www.google-analytics.com/ga.js causes an SSL security warning in IE6 on HTTPS pages. He continues:

I thought it was solely a geographic performance issue, but it turns out the browser issue is the main reason.

So now we know :)

MrBester wrote on :

Is it just me or does the insertion rely on the presence of a script tag somewhere in the page? I know it is unlikely that there is a page nowadays that doesn't have script on it,but:

document.getElementsByTagName('head')[0].appendChild(g);

guarantees it won't blow up whereas document.getElementsbyTagName('script') can return []. If there isn't a zero-th element, there can't be a .parentNode property...

Mathias wrote on :

MrBester: You actually need a <script> element to include the snippet or reference an external .js file containing the snippet, so in those cases document.getElementsbyTagName('script') will never return [].

However, you’re right that there are other (evil) ways to include the snippet — for example, by adding an onload attribute to the body element — but I don’t think anyone will ever complain that Google doesn’t handle these edge cases.

Christof wrote on :

Have you tried to simplify…

ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';

…to…

ga.src = '//www.google-analytics.com/ga.js';

? This way HTTP/HTTPS should be used automatically. I tested this before and seems to work but may be I missed something…

Mathias wrote on :

Christof: Erm, that’s exactly what this snippet is doing.

You’re right that this causes HTTP/HTTPS to be used automatically, but the problem is the subdomain is different as well. For HTTP the subdomain is www, for HTTPS it’s ssl.

Christof wrote on :

Ah, did not notice the www/ssl switch, my fault. Point was that you could have lost the additional distinction with (…) ? … : ….

Leave a comment

Comment on “Optimizing the asynchronous Google Analytics snippet”

Some Markdown is allowed; HTML isn’t. Keyboard shortcuts are available.

It’s possible to add emphasis to text:

_Emphasize_ some terms. Perhaps you’d rather use **strong emphasis** instead?

Select some text and press + I on Mac or Ctrl + I on Windows to make it italic. For bold text, use + B or Ctrl + B.

To create links:

Here’s an inline link to [Google](http://www.google.com/).

If the link itself is not descriptive enough to tell users where they’re going, you might want to create a link with a title attribute, which will show up on hover:

Here’s a [poorly-named link](http://www.google.com/ "Google").

Use backticks (`) to create an inline <code> span:

In HTML, the `p` element represents a paragraph.

Select some inline text and press + K on Mac or Ctrl + K on Windows to make it a <code> span.

Indent four spaces to create an escaped <pre><code> block:

    printf("goodbye world!");  /* his suicide note
was in C */

Select a block of text (more than one line) and press + K on Mac or Ctrl + K on Windows to make it a preformatted <code> block.

Quoting text can be done as follows:

> Lorem iPad dolor sit amet, consectetur Apple adipisicing elit,
> sed do eiusmod incididunt ut labore et dolore magna aliqua Shenzhen.
> Ut enim ad minim veniam, quis nostrud no multi-tasking ullamco laboris
> nisi ut aliquip iPad ex ea commodo consequat.

Select a block of text and press + E on Mac or Ctrl + E on Windows to make it a <blockquote>.