Laravel Components

Part 1

Create a bootstrap project:

Create a new view under new components folder called master.blade.php. This HTML template taken from Bootstrap:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="https://getbootstrap.com/docs/4.0/assets/img/favicons/favicon.ico">

    <title>Blog Template for Bootstrap</title>

    <link rel="canonical" href="https://getbootstrap.com/docs/4.0/examples/blog/">

    <!-- Bootstrap core CSS -->
    <link href="https://getbootstrap.com/docs/4.0/dist/css/bootstrap.min.css" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="https://fonts.googleapis.com/css?family=Playfair+Display:700,900" rel="stylesheet">
    <link href="{{asset('css/app.css')}}" rel="stylesheet">
    <link href="{{asset('css/blog.css')}}" rel="stylesheet">
  </head>

  <body>

    <div class="container">
      <header class="blog-header py-3">
        <div class="row flex-nowrap justify-content-between align-items-center">
          <div class="col-4 pt-1">
            <a class="text-muted" href="#">Subscribe</a>
          </div>
          <div class="col-4 text-center">
            <a class="blog-header-logo text-dark" href="#">Large</a>
          </div>
          <div class="col-4 d-flex justify-content-end align-items-center">
            <a class="text-muted" href="#">
              <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mx-3"><circle cx="10.5" cy="10.5" r="7.5"></circle><line x1="21" y1="21" x2="15.8" y2="15.8"></line></svg>
            </a>
            <a class="btn btn-sm btn-outline-secondary" href="#">Sign up</a>
          </div>
        </div>
      </header>

      <div class="nav-scroller py-1 mb-2">
        <nav class="nav d-flex justify-content-between">
          <a class="p-2 text-muted" href="#">World</a>
          <a class="p-2 text-muted" href="#">U.S.</a>
          <a class="p-2 text-muted" href="#">Technology</a>
          <a class="p-2 text-muted" href="#">Design</a>
          <a class="p-2 text-muted" href="#">Culture</a>
          <a class="p-2 text-muted" href="#">Business</a>
          <a class="p-2 text-muted" href="#">Politics</a>
          <a class="p-2 text-muted" href="#">Opinion</a>
          <a class="p-2 text-muted" href="#">Science</a>
          <a class="p-2 text-muted" href="#">Health</a>
          <a class="p-2 text-muted" href="#">Style</a>
          <a class="p-2 text-muted" href="#">Travel</a>
        </nav>
      </div>

      <div class="jumbotron p-3 p-md-5 text-white rounded bg-dark">
        <div class="col-md-6 px-0">
          <h1 class="display-4 font-italic">Title of a longer featured blog post</h1>
          <p class="lead my-3">Multiple lines of text that form the lede, informing new readers quickly and efficiently about what's most interesting in this post's contents.</p>
          <p class="lead mb-0"><a href="#" class="text-white font-weight-bold">Continue reading...</a></p>
        </div>
      </div>
    </div>

    <main role="main" class="container">
      <div class="row">
        <div class="col-md-8 blog-main">
          <h3 class="pb-3 mb-4 font-italic border-bottom">
            From the Firehose
          </h3>

          <div class="blog-post">
            <h2 class="blog-post-title">Sample blog post</h2>
            <p class="blog-post-meta">January 1, 2014 by <a href="#">Mark</a></p>

            <p>This blog post shows a few different types of content that's supported and styled with Bootstrap. Basic typography, images, and code are all supported.</p>
            <hr>
            <p>Cum sociis natoque penatibus et magnis <a href="#">dis parturient montes</a>, nascetur ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis consectetur purus sit amet fermentum.</p>
            <blockquote>
              <p>Curabitur blandit tempus porttitor. <strong>Nullam quis risus eget urna mollis</strong> ornare vel eu leo. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
            </blockquote>
            <p>Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
            <h2>Heading</h2>
            <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
            <h3>Sub-heading</h3>
            <p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</p>
            <pre><code>Example code block</code></pre>
            <p>Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa.</p>
            <h3>Sub-heading</h3>
            <p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
            <ul>
              <li>Praesent commodo cursus magna, vel scelerisque nisl consectetur et.</li>
              <li>Donec id elit non mi porta gravida at eget metus.</li>
              <li>Nulla vitae elit libero, a pharetra augue.</li>
            </ul>
            <p>Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue.</p>
            <ol>
              <li>Vestibulum id ligula porta felis euismod semper.</li>
              <li>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</li>
              <li>Maecenas sed diam eget risus varius blandit sit amet non magna.</li>
            </ol>
            <p>Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis.</p>
          </div><!-- /.blog-post -->

          <div class="blog-post">
            <h2 class="blog-post-title">Another blog post</h2>
            <p class="blog-post-meta">December 23, 2013 by <a href="#">Jacob</a></p>

            <p>Cum sociis natoque penatibus et magnis <a href="#">dis parturient montes</a>, nascetur ridiculus mus. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed posuere consectetur est at lobortis. Cras mattis consectetur purus sit amet fermentum.</p>
            <blockquote>
              <p>Curabitur blandit tempus porttitor. <strong>Nullam quis risus eget urna mollis</strong> ornare vel eu leo. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
            </blockquote>
            <p>Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
            <p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.</p>
          </div><!-- /.blog-post -->

          <div class="blog-post">
            <h2 class="blog-post-title">New feature</h2>
            <p class="blog-post-meta">December 14, 2013 by <a href="#">Chris</a></p>

            <p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
            <ul>
              <li>Praesent commodo cursus magna, vel scelerisque nisl consectetur et.</li>
              <li>Donec id elit non mi porta gravida at eget metus.</li>
              <li>Nulla vitae elit libero, a pharetra augue.</li>
            </ul>
            <p>Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
            <p>Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue.</p>
          </div><!-- /.blog-post -->

          <nav class="blog-pagination">
            <a class="btn btn-outline-primary" href="#">Older</a>
            <a class="btn btn-outline-secondary disabled" href="#">Newer</a>
          </nav>

        </div><!-- /.blog-main -->

        <aside class="col-md-4 blog-sidebar">
          <div class="p-3 mb-3 bg-light rounded">
            <h4 class="font-italic">About</h4>
            <p class="mb-0">Etiam porta <em>sem malesuada magna</em> mollis euismod. Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.</p>
          </div>

          <div class="p-3">
            <h4 class="font-italic">Archives</h4>
            <ol class="list-unstyled mb-0">
              <li><a href="#">March 2014</a></li>
              <li><a href="#">February 2014</a></li>
              <li><a href="#">January 2014</a></li>
              <li><a href="#">December 2013</a></li>
              <li><a href="#">November 2013</a></li>
              <li><a href="#">October 2013</a></li>
              <li><a href="#">September 2013</a></li>
              <li><a href="#">August 2013</a></li>
              <li><a href="#">July 2013</a></li>
              <li><a href="#">June 2013</a></li>
              <li><a href="#">May 2013</a></li>
              <li><a href="#">April 2013</a></li>
            </ol>
          </div>

          <div class="p-3">
            <h4 class="font-italic">Elsewhere</h4>
            <ol class="list-unstyled">
              <li><a href="#">GitHub</a></li>
              <li><a href="#">Twitter</a></li>
              <li><a href="#">Facebook</a></li>
            </ol>
          </div>
        </aside><!-- /.blog-sidebar -->

      </div><!-- /.row -->

    </main><!-- /.container -->

    <footer class="blog-footer">
      <p>Blog template built for <a href="https://getbootstrap.com/">Bootstrap</a> by <a href="https://twitter.com/mdo">@mdo</a>.</p>
      <p>
        <a href="#">Back to top</a>
      </p>
    </footer>

    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery-slim.min.js"><\/script>')</script>
    <script src="../../assets/js/vendor/popper.min.js"></script>
    <script src="../../dist/js/bootstrap.min.js"></script>
    <script src="../../assets/js/vendor/holder.min.js"></script>
    <script>
      Holder.addTheme('thumb', {
        bg: '#55595c',
        fg: '#eceeef',
        text: 'Thumbnail'
      });
    </script>
  </body>
</html>

In the welcome view replace with a component like this:

<x-master>
</x-master>

Part 2

Cut everything inside blog-main class of master.blade.php and add inside these tags of welcome.blade.php

<x-master>
    @section('main-content')
    Code here
    @endsection
</x-master>

Between blog-main class will now look like:

        <div class="col-md-8 blog-main">
          @yield('main-content')
        </div>

You can further make the main content contain another component by creating a new component called <x-test></x-test> inside the new section. Then in components folder create a template called test.blade.php and add the content you want.

Part 3

Pass data to components

Open routes to add proper controller to the route:

Route::get('/', [App\Http\Controllers\HomeController::class, 'index']);

In home.blade.php add this:

<x-test :users="$users"></x-test>

In your test.blade.php add this:

@foreach($users as $user)
    {{$user->name}}
@endforeach

And finally add this to the HomeController.php

use App\Models\User;
    public function index()
    {
        $users = User::all();
        return view('home', ['users' => $users]);
        
    }

Show/Hide by toggling class

import React, { useState } from 'react';
import { BrowserRouter } from 'react-router-dom';
import './App.css';

function App() {
	var [showChat, setShowChat ] = useState('hide');
	var [showButton , setShowButton ] = useState('show');
	function handleChatShow() {
		setShowChat((showChat === "hide") ? "show" : "hide");
		setShowButton((showChat === "hide") ? "hide" : "show");
	}
	return (
		<>
			<button className={"open-button "+showButton} id="openchat" onClick={handleChatShow}></button>
			<div className={"chat_box form-container "+showChat} id="chatbubble">
				<button type="button" className="btn cancel" onClick={handleChatShow}>—</button>
				Text here
			</div>
		</>
	);
}
export default App;

Basic React Login using External API

API

We will use a free Login API from MeCallAPI.com with the following detail:

{
    "username": "karn.yong@mecallapi.com",
    "password": "mecallapi"
}
  • Response:
{
    "status": "ok",
    "message": "Logged in",
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "user": {
        "id": 1,
        "fname": "Karn",
        "lname": "Yong",
        "username": "karn.yong@mecallapi.com",
        "email": "karn.yong@mecallapi.com",
        "avatar": "https://www.mecallapi.com/users/1.png"
    }
}

For more APIs with populated fake data, such as user list and user profile authentication with JWT, these can be found at MeCallAPI.com.

Add packages

We will add the following packages to our application:

  • Material UI
  • react-router-dom
  • Sweetalert
npm i @material-ui/core @material-ui/icons react-router-dom sweetalert

Create screen for Sign-in

Create Sigin.js (From line 41 is the use of login API from MeCallAPI.com)

import React, { useState } from 'react';
import swal from 'sweetalert';
import './Login.css';

async function loginUser(credentials) {
	return fetch('https://www.mecallapi.com/api/login', {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json'
		},
		body: JSON.stringify(credentials)
	})
    .then(data => data.json())
}

const Login = props => {
	const [username, setUserName] = useState();
	const [password, setPassword] = useState();
	const handleSubmit = async e => {
		e.preventDefault();
		const response = await loginUser({
			username,
			password
		});
		if ('accessToken' in response) {
			swal("Success", response.message, "success", {
				buttons: false,
				timer: 2000,
			})
			.then((value) => {
				localStorage.setItem('accessToken', response['accessToken']);
				localStorage.setItem('user', JSON.stringify(response['user']));
				window.location.href = "/admin";
			});
		} else {
			swal("Failed", response.message, "error");
		}
	}
	return (
		<>
			<div id="page-wrap" className="my-4 mx-4">
				<h1>Login</h1>
				<form noValidate onSubmit={handleSubmit}>
				  <div className="mb-3">
					<label for="email" className="form-label">Email address</label>
					<input
					  required
					  id="email"
					  name="email"
					  className="form-control"
					  aria-describedby="emailHelp"
					  onChange={e => setUserName(e.target.value)}
					/>
				  </div>
				  <div className="mb-3">
					<label for="password" className="form-label">Password</label>
					<input
					  id="password"
					  name="password"
					  type="password"
					  className="form-control"
					  onChange={e => setPassword(e.target.value)}
					/>
				  </div>
				  <div className="mb-3 form-check">
					<input type="checkbox" className="form-check-input" id="exampleCheck1" />
					<label className="form-check-label" for="exampleCheck1">Remember me</label>
				  </div>
				  <button type="submit" className="btn btn-primary">Submit</button>
				</form>
			</div>
		</>
	)
}
export default Login;

Now you can update your menu to show correct menu items for logged in/out states:

import React, { lazy } from 'react';
import { Link } from 'react-router-dom';
import './Sidebar.css';

const Menu = lazy(() => import('react-burger-menu/lib/menus/elastic'));


const Sidebar = props => {
	const isLoggedIn = localStorage.getItem('accessToken');
    
	const handleLogout = () => {
		localStorage.removeItem("accessToken");
		localStorage.removeItem("user");
		window.location.href = "/";
	};
	
	let adminLink, loggedLink;
    if (isLoggedIn) {
		adminLink = <Link className="menu-item" to="/admin">Admin</Link>;
		loggedLink = <div className="menu-item" onClick={handleLogout}>Logout</div>;
    } else {
		loggedLink = <Link className="menu-item" to="/login">Login</Link>;
    }
	
	return (
		<Menu>
      <Link className="menu-item" to="/">
        Home
      </Link>
      <Link className="menu-item" to="/favourites">
        Favourites
      </Link>
	  {adminLink}
	  {loggedLink}
    </Menu>
	);
}
export default Sidebar;

Toggle elements inside list of items

Hold ID of each row inside an array that you set when clicked. Use if statement inside JSX to check whether array has the ID value and show selected icon if it does, empty icon if it doesn’t.

const List = props => {
	var pagedList = props.pagedList;
	const [isFave, setIsFave] = useState([]);
	
	const selectFavourite = e => {
		const { id } = e.target;
		if(isFave.includes(id)){
			setIsFave(isFave.filter(item => item !== id));
		}else{
			setIsFave([...isFave, id]);
		}
    	}
	console.log(isFave);
    	return ( 
		<>
		{pagedList.map((row,i) => {
			return (
				<div key={i} className="list-card row">
					<div className="col-12 col-md g-0 list-content">
					<div 
						className="favouriteButton" 
						id={row.id_pg} 
						onClick={selectFavourite}
					>
						{isFave.includes(row.id_pg) ? <FavoriteIcon /> : <FavoriteBorderIcon /> }
					</div>
					</div>
				</div>
			);
		})}
		</>
    	);  
}  
export default List

Check all checkboxes

We get the checkboxes from an array of results called list. We use one of the array values to set the checked states and hold it in a variable called isCheck.

All checkboxes in the list have an onChange function called selectCheckbox which sets the checkbox ID in the isCheck array when checked and removes when unchecked.

IMPORTANT: To control the selecting all checkboxes, you need to add a checked attribute onto each individual checkbox that checks if the isCheck array includes the current array value.

In the main component below we have the select all checkbox. We have an onChange function called selectAll which toggles the true/false value for isCheckAll variable. Then it sets all checkboxes or empties all checkboxes array of isCheck depending on the true/false value of isCheckAll.

import React, { lazy, useEffect, useState } from 'react';
import axios from 'axios';

const MyList = lazy(() => import('../../components/MyList/MyList'));

const YourComponent = props => {
	const [isCheckAll, setIsCheckAll] = useState(false);
	const [isCheck, setIsCheck] = useState([]);
	var [list, setList] = useState([]);
	useEffect(() => {
		getRows()
	}, []);
	function getRows() {
		let getUrl = 'https://domain.com/getrows.php';
		axios({
			method: "get",
			url: getUrl,
		}).then((response) => {
			let data = response.data;
			setList(data.listRows);
		}, (error) => {
			console.log(error)
		})
	}
	const selectAll = e => {
		setIsCheckAll(!isCheckAll);
		setIsCheck(list.map(row => row.id_pg));
		if (isCheckAll) {
		  setIsCheck([]);
		}
	};
	return (
		<>
			<input
				id="selectAllCheckbox"
				name="selectAllCheckbox"
				type="checkbox"
				value=""
				className="form-check-input"
				onChange={selectAll}
			/>
			{list.length > 0 &&
			<MyList 
				list={list} 
				isCheck={isCheck}
				setIsCheck={setIsCheck.bind(this)} 
			/>
			}
		</>
	)
}
export default YourComponent;

Next create the checkboxes list component:

import React from 'react'
import './List.css'; 

const MyList = props => {
	var list= props.list;
	const isCheck = props.isCheck;

	const selectCheckbox = e => {
		const { id, checked } = e.target;
		props.setIsCheck([...isCheck, id]);
		if (!checked) {
			props.setIsCheck(isCheck.filter(item => item !== id));
		}
	};
	console.log(isCheck);
    return ( 
		<>
		{list.map((row,i) => {
			return (
			<div key={i} className="row">
				<div className="col-1">
					<input
						id={row.id_pg}
						name="select"
						type="checkbox"
						className="form-check-input"
						onChange={selectCheckbox}
						checked={isCheck.includes(row.id_pg)}
					/>
				</div>
				<div className="col-11">
				{row.name}
				</div>
			</div>
			);
		})}
		</>
    );  
}  
export default MyList

Rendering raw html with reactjs

dangerouslySetInnerHTML is React’s replacement for using innerHTML in the browser DOM. In general, setting HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) attack.

It is better/safer to sanitise your raw HTML (using e.g., DOMPurify) before injecting it into the DOM via dangerouslySetInnerHTML.

DOMPurify – a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG. DOMPurify works with a secure default, but offers a lot of configurability and hooks.

import React from 'react'
import * as DOMPurify from 'dompurify';

const dropdownMenu = `
<div class="dropdown">
  <button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-expanded="true">
    Dropdown
    <span class="caret"></span>
  </button>
  <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Action</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Another action</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Something else here</a></li>
    <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Separated link</a></li>
  </ul>
</div>
`

const YourComponent = props => {
  <div>
    { <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(dropdownMenu) }} /> }
  </div>
}
export default YourComponent;
If the HTML is coming from user input, make sure it is sanitized!

Add new GA4 script to WordPress

<?php
add_action('wp_print_scripts', 'inline_scripts');
function inline_scripts(){
	//Only track if not logged in or is a customer
	global $current_user;
	if ( !isset( $current_user->roles[0] ) || $current_user->roles[0] == 'customer' ) {
	?>
	<!-- Google tag (gtag.js) -->
	<script async src="https://www.googletagmanager.com/gtag/js?id=G-IDHERE"></script>
	<?php
	}
	?>
	<script>
	  window.dataLayer = window.dataLayer || [];
	  function gtag(){dataLayer.push(arguments);}
	  gtag('js', new Date());
	  gtag('config', 'G-IDHERE');
	</script>
	<?php
}

Integrate Google Search Console with GA4

Understanding Organic Search data is important for any SEO or web analyst. In Universal Analytics, you could integrate your Google Search Console property with your GA property. You can now also do this for Google Analytics 4 properties!

I’ll show you how to get this set up in this step-by-step guide.

Step 1: In your GA4 admin section, open up the Search Console integration tab

Head on over to the admin section of your Google Analytics 4 property and under the property column, scroll down and under the “Product Linking” section you’ll see a new option for “Search Console Linking”

Clicking in, you’ll be taken to an overview screen which shows your linking options. You’ll want to click the blue “Link” button in the upper right hand corner in order to start setting up a new linking.

Step 2: Set up the linking

This is a quick 3 step process, shown in the walkthrough of the screenshot below.

2.1 Choose your Search Console property. When you click “Choose accounts”, you’ll be taken to a screen which shows you all of the Search Console properties you have access to based on your account permissions.

Important: You must be an admin (‘Edit’ access) of the GA4 property and a verified site owner of the Search Console Property in order to set up the linking. This also assumes you already have a Search Console property set up for your domain, if not, you’ll need to go through the process of setting it up and verifying your domain ownership before you will be able to link Search Console to Google Analytics 4. There is an “Add a property” link towards the top left of this screen if you still need to do that step.

This linking screen will show you which Search Console properties you are a verified site owner of and can therefore link to. You’ll see that in the screenshot below, I’m choosing to link the property for kristaseiden.com. I am also a verified site owner of ksdigital.co, but as you can see, that Search Console property is already linked to a GA4 property and is therefore not available to choose.

Once you’ve selected the property you wish to link, hit confirm.

Finally, you’ll be back on the overview screen and will see the Search Console property you’ve selected listed. Now you can hit “Next” to move onto step 2.2.

2.2 Select a Web Stream. In this step, you’ll select the GA4 web stream you want to link your Search Console property to. In this case, I only have one GA4 web stream in this property, so it’s an easy choice. 

Note that it’s recommended to only have 1 web stream per property, but if you have multiple, you’ll need to choose and could end up losing out on valuable linking data since you can only link a Search Console property to 1 web stream. 

Once you select the web stream you want to link, you’ll be taken back to the overview screen showing your selection. Then hit “Next” to continue.

2.3 Review and submit. You’re almost done! You’ll see an overview screen which shows the selections you made in the first two steps. If these selections look correct to you, hit “Submit”

You’ll then get taken to a confirmation screen which will show you that you have successfully linked your Search Console property to your GA4 web stream. Yay!

Now that the link is created, it’s time to reap the rewards!

Step 3: Set up Reporting

Reporting is not automatically added to the left hand nav once you’ve set up this integration. Instead, once you’ve set up the integration between Search Console and GA4, you’ll see a new report collection available in the “Library” section of GA4 (note – you’ll need to be an admin on the GA4 property to see this option). 

You can then click on the three vertical dots in the upper right hand corner of the Search Console report collection to edit, rename, delete, or publish the collection. 

Clicking “Publish” will push the reporting collection live and show it in the left hand nav bar. Once it’s live, you’ll be able to click into the 2 reports provided and interact with them.

I’d like to go ahead and change the names of these reports to be more meaningful to me, so I can scroll down in the report list of the library section, find the reports I’m interested in, click the three dots menu, and hit rename:

I’m going to rename “Queries” to “SC: Organic Keywords” and “Google organic search traffic” to “SC: Landing Page” because these names are more meaningful to me, relating directly to what I see in these reports. I also re-ordered them in the collection interface. Now my left nav looks like this:

If we click into either of these reports, we can now see the data being collected.

“SC: Landing Page” (aka “Google organic search traffic”)

(note this data comes from www.ksdigital.co instead of www.kristaseiden.com for the purposes of showing better data)

Here you can see that I now have a robust report showing “Landing page” as the key dimension. WOO WOO GA4 finally has Landing page! All the SEOs can breathe a little easier now  

This report shows a number of key metrics such as Organic google search clicks, Organic google search impressions, Organic google search click through rate, Organic google search average position, users, engaged sessions, engagement rate, avg engagement time, event count, conversions, and Ad revenue. These metrics are based on the dimension of Landing Page, but you also have the opportunity to change the primary dimension and add a secondary dimension, similar to how all other table reports work in GA4. If you don’t want to see/don’t need any of these metrics or dimensions, you can always edit this report to remove any unnecessary bulk. 

“SC: Organic Keywords” (aka “Queries”)

(note this data comes from www.ksdigital.co instead of www.kristaseiden.com for the purposes of showing better data)

This report shows me my organic google search queries, pulled in from Google Search console. This includes metrics such as Organic google search clicks, Organic google search impressions, Organic google search click through rate, and Organic google search average position. 

Many of you may remember that prior to October 2011, Google Analytics actually collected organic search queries itself, but when Google (and soon most other search engines) encrypted search, this data almost entirely disappeared. Integrating with Search Console is the only way to see some of this data these days.

This data is GOLD because it can help you understand how users are searching for and finding you online. It can help you understand searcher intent, and can in turn help you tailor your site content to better meet the needs of people who maybe searching for you. 

Keep in mind: Google Search Console keeps data only for the last 16 months, which means that reports in Google Analytics will include a maximum of 16 months of data. Search Console data is available in Search Console and in Analytics 48 hours after it is collected by Search Console.

The bottom line

There is some very valuable data that will help you make more informed decisions for your business that you can add to your GA4 property by integrating with Google Search Console. This integration is now live and is super easy to set up in just 3 simple steps. So what are you waiting for?

Want to learn even more about GA4? Head on over to https://ksdigital.co/academy/ to check out our course offerings to help you Master Google Analytics 4.

Regex

Short for regular expression, a regex is a string of text that lets you create patterns that help match, locate, and manage text.

Examples

Wildcard search in array with regex in PHP

$array = array('Michael','Marian','Martina');
$matching_letters = 'ri'; 
$array = preg_grep("/{$matching_letters}/i", $array);

Websites for regex pattern matching

Regex101.com 
PHPLiveRegex.com