I have a firebase website hosted in Firebase hosting and built using Ionic Framework (Angular 8.2).
The web app uses a client-side rendering (default Angular 8 behavior) and does not load any web app content to the browser’s HTML. This causes significant issues in our SEO, page indexing, and page ranking.
See the HTML source code below of the default landing page for example (Note the <app-root></app-root>
tag):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>GAGA</title>
<base href="/" />
<meta
name="viewport"
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<!-- <meta http-equiv="Content-Security-Policy"
content="default-src *; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'; media-src *; img-src * filesystem: data:" /> -->
<link rel="icon" type="image/png" href="assets/icon/favicon.png" />
<!-- add to homescreen for ios -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="theme-color" content="#1976d2" />
<meta name="mobile-web-app-capable" content="yes">
<!-- <link rel="manifest" href="manifest.webmanifest" /> -->
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2048-2732.png" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2732-2048.png" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1668-2388.png" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2388-1668.png" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1668-2224.png" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2224-1668.png" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1536-2048.png" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2048-1536.png" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1242-2688.png" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2688-1242.png" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1125-2436.png" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2436-1125.png" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-828-1792.png" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1792-828.png" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1242-2208.png" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2208-1242.png" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-750-1334.png" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1334-750.png" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-640-1136.png" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
<link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1136-640.png" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
<link rel="apple-touch-icon" sizes="180x180" href="assets/icons/apple-icon-180.png" />
<link rel="apple-touch-icon" sizes="167x167" href="assets/icons/apple-icon-167.png" />
<link rel="apple-touch-icon" sizes="152x152" href="assets/icons/apple-icon-152.png" />
<link rel="apple-touch-icon" sizes="120x120" href="assets/icons/apple-icon-120.png" />
<!-- Tamara -->
<script async src="https://cdn.tamara.co/widget/tamara-widget.min.js"></script>
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-159202064-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-159202064-1');
</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=AW-604302093"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'AW-604302093');
</script>
<link rel="stylesheet" href="styles.eb3052c834c7da8f09b2.css"></head>
<body>
<app-root></app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript>
<!-- Face Pixel Code -->
<script>
!(function (f, b, e, v, n, t, s) {
if (f.fbq) return;
n = f.fbq = function () {
n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments);
};
if (!f._fbq) f._fbq = n;
n.push = n;
n.loaded = !0;
n.version = '2.0';
n.queue = [];
t = b.createElement(e);
t.async = !0;
t.src = v;
s = b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t, s);
})(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');
function logEventFb(event, obj) {
fbq('track', event, obj);
}
function initFacebookPixel(pixelId) {
fbq('init', pixelId);
}
function setPageViewFb(pageView) {
fbq('track', pageView);
console.log('track view:', pageView);
}
function shareViaSocial(text) {
navigator.share({
text
});
}
</script>
<!-- Snap Pixel Code -->
<script type="text/javascript">
(function (e, t, n) {
if (e.snaptr) return;
var a = (e.snaptr = function () {
a.handleRequest ? a.handleRequest.apply(a, arguments) : a.queue.push(arguments);
});
a.queue = [];
var s = 'script';
r = t.createElement(s);
r.async = !0;
r.src = n;
var u = t.getElementsByTagName(s)[0];
u.parentNode.insertBefore(r, u);
})(window, document, 'https://sc-static.net/scevent.min.js');
function initSnapchat(snapId, userEmail) {
snaptr('init', snapId, {
user_email: userEmail
});
}
function logEventSnapchat(pageView, obj) {
snaptr('track', pageView, obj);
}
</script>
<!-- End Snap Pixel Code -->
<!-- TikTok Pixel Code -->
<script>
!(function (w, d, t) {
w.TiktokAnalyticsObject = t;
var ttq = (w[t] = w[t] || []);
(ttq.methods = [
'page',
'track',
'identify',
'instances',
'debug',
'on',
'off',
'once',
'ready',
'alias',
'group',
'enableCookie',
'disableCookie'
]),
(ttq.setAndDefer = function (t, e) {
t[e] = function () {
t.push([e].concat(Array.prototype.slice.call(arguments, 0)));
};
});
for (var i = 0; i < ttq.methods.length; i++) ttq.setAndDefer(ttq, ttq.methods[i]);
(ttq.instance = function (t) {
for (var e = ttq._i[t] || [], n = 0; n < ttq.methods.length; n++)
ttq.setAndDefer(e, ttq.methods[n]);
return e;
}),
(ttq.load = function (e, n) {
var i = 'https://analytics.tiktok.com/i18n/pixel/events.js';
(ttq._i = ttq._i || {}),
(ttq._i[e] = []),
(ttq._i[e]._u = i),
(ttq._t = ttq._t || {}),
(ttq._t[e] = +new Date()),
(ttq._o = ttq._o || {}),
(ttq._o[e] = n || {});
var o = document.createElement('script');
(o.type = 'text/javascript'), (o.async = !0), (o.src = i + '?sdkid=' + e + '&lib=' + t);
var a = document.getElementsByTagName('script')[0];
a.parentNode.insertBefore(o, a);
});
})(window, document, 'ttq');
function initTiktokPixel(pixelId) {
ttq.load(pixelId);
}
function trackEvent(eventName, obj) {
ttq.track(eventName, obj);
}
</script>
<!-- End TikTok Pixel Code -->
<script>
function getURLParameter(name) {
console.log('location.search = ', location.search);
return (
decodeURIComponent(
(new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [
,
''
])[1].replace(new RegExp('\+', 'g'), '%2B')
) || null
);
}
function locationHasChanged() {
if (location.href.indexOf('/booking/confirmation') != -1) {
var amount = getURLParameter('amount');
var transactionDocumentId = getURLParameter('transactionId');
var objGA = {
send_to: 'AW-604302093/RfE2CITy_P8BEI3Wk6AC',
value: amount,
currency: 'SAR',
transaction_id: transactionDocumentId
};
console.log('objG: ', objGA);
gtag('event', 'conversion', objGA);
}
}
let currentUrl = location.href;
const checkPageTransition = () => {
// requestAnimationFrame(() => {
if (currentUrl !== location.href) {
locationHasChanged();
}
currentUrl = location.href;
// }, true);
};
document.body.addEventListener('click', checkPageTransition);
window.addEventListener('load', function () {
console.log('LOAD ...');
});
</script>
<!-- Deep Link Adjust -->
<script>
// Function callback Custom-URL-scheme (Android).
function handleOpenURL(url) { sessionStorage.setItem('deepLinkAdjust', url) }
</script>
<script src="runtime-es2015.c2d98ede7b5e860b1261.js" type="module"></script><script src="runtime-es5.c2d98ede7b5e860b1261.js" nomodule defer></script><script src="polyfills-es5.8d609fa504ef0e3ff287.js" nomodule defer></script><script src="polyfills-es2015.7083b1229f85eaf01d71.js" type="module"></script><script src="scripts.fc3dae47aa26344b0dc0.js" defer></script><script src="main-es2015.81549ae55de7e98047a3.js" type="module"></script><script src="main-es5.81549ae55de7e98047a3.js" nomodule defer></script></body>
</html>
I am thinking about a way to inject HTML (text content) before the page is served to the client side in a meta tag or inside the body. So what is the best way to achieve this?
2
Answers
Make sure you have Firebase CLI installed on your machine. If not, you can install it with
$npm install -g firebase-tools
.
Initialize it folder using
$firebase init.
Choose the "Functions" option when prompted, and follow the setup instructions.
Then write a Cloud Function to modify the HTML content
Then deploy it & set up like so
To inject HTML before a Firebase website is served to the client side, you can use Cloud Functions for Firebase. Cloud Functions allows you to run server-side JavaScript code in response to specific events, such as an HTTP request. By using a Cloud Function, you can intercept the response and modify the HTML content before it is sent to the client.
Here’s a step-by-step guide on how to achieve this:
Set up Firebase project and Cloud Functions: If you haven’t already, create a Firebase project and set up Cloud Functions for that project. You can do this using the Firebase CLI by running
firebase init functions
in your project directory.Create a Cloud Function: In your
functions/index.js
file, define a Cloud Function that will intercept HTTP requests. For example:firebase.json
file:Now, whenever a client requests a page from your Firebase Hosting site, the request will be intercepted by the
interceptHtml
Cloud Function, which will modify the HTML content before it’s sent to the client.Please note that modifying the HTML content on the server-side could introduce complexity and may not always be the best solution. Consider carefully whether server-side modification is necessary or if client-side manipulation using JavaScript is more appropriate for your use case. Additionally, keep in mind that server-side rendering or other advanced techniques might be better suited for complex use cases.
You can also try Server side rendering using:
Replace
[your-client-project-name]
with the name of your existing Angular app project.Implement Server-Side Data Preloading (Optional): If you need to fetch data from APIs or perform any server-side operations before rendering the app, you can do that in the server-side app. This is useful for scenarios where you want to include dynamic content in the rendered HTML. Refer to Angular Universal documentation for more details on data preloading.
Inject Content: In your server-side app, you can inject content into the rendered HTML before sending it to the client. For example, you can use template literals or DOM manipulation to add the desired HTML content. The injected content can be added to a meta tag, the body, or any other part of the HTML.
Build and Deploy: Build your server-side app and deploy it along with your Firebase hosting configuration. Make sure to route traffic to the server-side app instead of the default client-side app.