diff --git a/src/modules/recipe/apiSpread/__tests__/apiSpread.test.js b/src/modules/recipe/apiSpread/__tests__/apiSpread.test.js
new file mode 100644
index 00000000..c09c09c5
--- /dev/null
+++ b/src/modules/recipe/apiSpread/__tests__/apiSpread.test.js
@@ -0,0 +1,78 @@
+import { createElement } from 'lwc';
+import ApiSpread from 'recipe/apiSpread';
+
+describe('recipe-api-spread', () => {
+ afterEach(() => {
+ // The jsdom instance is shared across test cases in a single file so reset the DOM
+ while (document.body.firstChild) {
+ document.body.removeChild(document.body.firstChild);
+ }
+ });
+
+ // Helper function to wait until the microtask queue is empty.
+ async function flushPromises() {
+ return Promise.resolve();
+ }
+
+ // Helper function to set values in the ui-input elements
+ function setInputElementValues(element, firstName, lastName) {
+ element.shadowRoot.querySelectorAll('ui-input').forEach((input) => {
+ if (firstName && input.name === 'firstName') {
+ input.value = firstName;
+ input.dispatchEvent(new CustomEvent('change'));
+ } else if (lastName && input.name === 'lastName') {
+ input.value = lastName;
+ input.dispatchEvent(new CustomEvent('change'));
+ }
+ });
+ }
+
+ it('renders recipe-child component with default values', () => {
+ // Create component
+ const element = createElement('recipe-api-spread', {
+ is: ApiSpread
+ });
+ document.body.appendChild(element);
+
+ // Query child component
+ const childEl = element.shadowRoot.querySelector('recipe-child');
+ expect(childEl).not.toBeNull();
+
+ // Validation for default values passed down to child component
+ expect(childEl.firstName).toBe('Amy');
+ expect(childEl.lastName).toBe('Taylor');
+ });
+
+ it('changes the value of the recipe-child component based on user input', async () => {
+ // Create component
+ const element = createElement('recipe-api-spread', {
+ is: ApiSpread
+ });
+ document.body.appendChild(element);
+
+ // Set values in the ui-input elements
+ setInputElementValues(element, 'Jennifer', 'Wu');
+
+ // Query child component
+ const childEl = element.shadowRoot.querySelector('recipe-child');
+ expect(childEl).not.toBeNull();
+
+ // Wait for any asynchronous DOM updates
+ await flushPromises();
+
+ // Validation for values of lwc spread properties passed down to child component
+ expect(childEl.firstName).toBe('Jennifer');
+ expect(childEl.lastName).toBe('Wu');
+ });
+
+ it('is accessible', async () => {
+ // Create component
+ const element = createElement('recipe-api-spread', {
+ is: ApiSpread
+ });
+ document.body.appendChild(element);
+
+ // Check accessibility
+ await expect(element).toBeAccessible();
+ });
+});
diff --git a/src/modules/recipe/apiSpread/apiSpread.css b/src/modules/recipe/apiSpread/apiSpread.css
new file mode 100644
index 00000000..1f1beb1d
--- /dev/null
+++ b/src/modules/recipe/apiSpread/apiSpread.css
@@ -0,0 +1,18 @@
+recipe-child {
+ position: relative;
+ border: solid 1px #ecebea;
+ border-radius: 4px;
+ display: block;
+ padding: 14px 8px 8px 8px;
+ margin-top: 16px;
+}
+
+recipe-child:before {
+ content: 'recipe-child';
+ color: #dddbda;
+ position: absolute;
+ top: -16px;
+ left: 4px;
+ background-color: #ffffff;
+ padding: 0 4px;
+}
diff --git a/src/modules/recipe/apiSpread/apiSpread.html b/src/modules/recipe/apiSpread/apiSpread.html
new file mode 100644
index 00000000..e11f7daf
--- /dev/null
+++ b/src/modules/recipe/apiSpread/apiSpread.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+ Pass data to a child component using lwc:spread directive
+
+
+
diff --git a/src/modules/recipe/apiSpread/apiSpread.js b/src/modules/recipe/apiSpread/apiSpread.js
new file mode 100644
index 00000000..999c4f80
--- /dev/null
+++ b/src/modules/recipe/apiSpread/apiSpread.js
@@ -0,0 +1,16 @@
+import { LightningElement, track } from 'lwc';
+
+export default class ApiSpread extends LightningElement {
+ @track props = {
+ firstName: 'Amy',
+ lastName: 'Taylor'
+ };
+
+ handleFirstNameChange(event) {
+ this.props.firstName = event.target.value;
+ }
+
+ handleLastNameChange(event) {
+ this.props.lastName = event.target.value;
+ }
+}
diff --git a/src/modules/recipe/child/child.html b/src/modules/recipe/child/child.html
new file mode 100644
index 00000000..c35b8c96
--- /dev/null
+++ b/src/modules/recipe/child/child.html
@@ -0,0 +1 @@
+ Hello, {fullName}!
diff --git a/src/modules/recipe/child/child.js b/src/modules/recipe/child/child.js
new file mode 100644
index 00000000..98a9c918
--- /dev/null
+++ b/src/modules/recipe/child/child.js
@@ -0,0 +1,10 @@
+import { LightningElement, api } from 'lwc';
+
+export default class Child extends LightningElement {
+ @api firstName;
+ @api lastName;
+
+ get fullName() {
+ return `${this.firstName} ${this.lastName}`;
+ }
+}
diff --git a/src/modules/ui/app/app.html b/src/modules/ui/app/app.html
index 99a90812..6675d224 100644
--- a/src/modules/ui/app/app.html
+++ b/src/modules/ui/app/app.html
@@ -33,6 +33,7 @@
+