How to pass data from Laravel to a Vue/React SPA app

Adam Mackintosh
4 min readMay 26, 2020
Fig 1.0 — Source: https://learnappmaking.com/pass-data-between-view-controllers-swift-how-to/

I might not recommend using this pattern to pass hyper-sensitive information because window; however, I do like good object composition and function composition, so I appreciate the data flow channel that this pattern creates.

spa.blade.php (root layout file)

@php
$config = [
'appName' => config('app.name'),
'jwt' => [
'ttl' => config('jwt.ttl'),
'refresh_ttl' => config('jwt.refresh_ttl'),
],
];
@endphp
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head> ... </head>
<body>
<div id="app"> ... </div>
<script>
'use strict';
window.config = @json($config);
</script>
</body>
</html>

Once you have that technology, you can do stuff like this:

some-component.vue

export default {
methods: {
handleSomething() {
console.log('config', window.config.jwt);
},
},
};

or

this.$store.dispatch('auth/saveToken', {
token,
expires_in: window.config.jwt.ttl,
});

A type problem

For example, when JWT_TTL=60 is declared in the .env file, it shows up as a string "60" in JavaScript. You can fix that by typecasting:

@php
$config = [
'jwt' => [
'ttl' => (int)config('jwt.ttl'),
'refresh_ttl' => (int)config('jwt.refresh_ttl'),
],
];
@endphp

Also don’t forget about the unary plus operator in JavaScript:

// JS TRIVIA BONUS: unary is more performant than `Number()`
// because it omits one `if` condition in the compiler
console.log(+window.config.jwt.ttl);const number = '60';
console.log('number', +number, typeof +number);

Conclusion

In Laravel, anytime you want, you can effectively dump anything into one object, which is kind of an accumulation phase. Such an action is deterministic. This means it will work nicely for any read-only operations that occur at any moment in time afterwards.

NOTE: Because we are in the root layout file, we are creating this object at the last moment in time before all power is delegated into the #app div. It’s the moment in time when power shifts from server to client.

In Vue, you can read this object at any moment in time. The object should be immutable, but you could probably watch it reactively with computed props and/or a watcher.

The thing that I like about this pattern is that the contract between the server and client is explicit. It is a straight forward process to figure out:

  • where config('some.thing') is being used,
  • where window.config.some.thing is being used,
  • where exactly window.config stems from,
  • what exactly is going into window.config.

In the past, I’ve seen people use stuff like JavaScript::put(['some' => 'thing']) in Laravel, and I dislike usage of that because it makes the data flow contract between server and client implicit.

For example if someone does JavaScript::put(['data’ => Some::thing()]), and some JavaScript code is reading window.data, it can be extremely difficult to find the Laravel code responsible for populating that state. You might have to search the codebase for all instances of data or JavaScript::put( to find it. And it’s even worse if you don’t know use JavaScript; is being used in Laravel. Imagine a large codebase you just inherited.

We must be careful how we read and write to state in async JavaScript. This is why I prefer explicit contracts whenever reasonable. All of this logic is similar to passing props down in Vue or React, from parent to child. It’s easy to see how data gets into a component, and it helps to keep functions pure.

A contrasting way to pass data would be something like a global event emitter where some unknown component emits an event to nowhere specific and some unknown component commits to action based on that event.

Here’s an example of that contrasting way in Vue JS:

// component-a.vue
this.$root.$on('some-event-name', (id) => {
if (id === 1337) {
this.$store.dispatch('some-mutating-action', 'payload');
}
});
// component-b.vue
this.$root.$emit('some-event-name', 1337);

Bugs stemming from a pattern like that can in theory trend quickly towards difficult.

Therefore, just in case this Laravel to Vue/React data flow pattern is a rare gem, I should document it. After Laravel is done executing the root layout Blade template, it’s pretty much hands-off. Slap data down on the roof of that badboy, and you’re off to the async Promise races in JavaScript with some immutable state that came from Laravel. After that, all other client-side data stems from the client or a client-made API request. You can guarantee it.

EOF

--

--

Adam Mackintosh

I prefer to work near ES6+, node.js, microservices, Neo4j, React, React Native; I compose functions and avoid classes unless private state is desired.