{"id":43,"date":"2025-07-01T14:25:37","date_gmt":"2025-07-01T18:25:37","guid":{"rendered":"https:\/\/www.shawntgray.com\/blog\/?p=43"},"modified":"2025-07-01T14:26:08","modified_gmt":"2025-07-01T18:26:08","slug":"how-to-build-an-art-gallery-with-react-part-3-components","status":"publish","type":"post","link":"https:\/\/www.shawntgray.com\/blog\/how-to-build-an-art-gallery-with-react-part-3-components\/","title":{"rendered":"How to Build an Art Gallery with React \u2013 Part 3: Components"},"content":{"rendered":"\n<p>In this part, we&#8217;ll:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Discover the relationship between parent and child components and how styles, properties, and actions flow through the components<\/li>\n\n\n\n<li>Build the ImageCard and ImageGallery components and update the App component to properly use those components<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">The Parent-Child Relationship<\/h2>\n\n\n\n<p>For our React app, we will have this basic component tree:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>great-grandparent --- index.js\n  V                     V\ngrandparent --------- App.js \n  V                     V\nparent -------------- ImageGallery.js\n  V                     V\nchild --------------- ImageCard.js<\/code><\/pre>\n\n\n\n<p>Styles and properties <strong>flow from the topmost component<\/strong> and apply to each level underneath it. Each individual component can have its own batch of styles and properties that could add to or override its parent&#8217;s styles or properties.<\/p>\n\n\n\n<p>For example:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>index.js<\/strong> has the styles <em>background-color: red<\/em> and <em>color: green<\/em><\/li>\n\n\n\n<li><strong>App.js<\/strong> inherits the <em>background-color<\/em> and <em>color <\/em>from <strong>index.js<\/strong>, but overrides the <em>color <\/em>style to blue. The <strong>index.js<\/strong> <em>color <\/em>remains green.<\/li>\n\n\n\n<li><strong>ImageGallery.js<\/strong> inherits the unchanged <em>background-color<\/em> from <strong>App.js<\/strong> (flowing through from <strong>index.js<\/strong>) and the overwritten <em>color <\/em>styles. Here, the <em>background-color<\/em> is overwritten to purple.<\/li>\n\n\n\n<li><strong>ImageCard.js<\/strong> now inherits the styles <em>background-color: purple<\/em> and <em>color: blue<\/em>.<\/li>\n<\/ol>\n\n\n\n<p>Styles and properties flow in a top-down direction. Actions and reactions, however <strong>&#8220;bubble&#8221;<\/strong> from the bottom up. Say the<strong> ImageCard.js<\/strong> component includes a button with an <strong>onClick<\/strong> action that would change some a headline in <strong>App.js<\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>great-grandparent --- index.js\n  ^                     ^\ngrandparent --------- App.js \n  ^                     ^\nparent -------------- ImageGallery.js\n  ^                     ^\nchild --------------- ImageCard.js<\/code><\/pre>\n\n\n\n<ol class=\"wp-block-list\">\n<li>The user clicks the button in<strong> ImageCard.js<\/strong>. The action is captured as an <strong>event bubble<\/strong> in this component.<\/li>\n\n\n\n<li>The event bubble rises to <strong>ImageGallery.js<\/strong>. No functions in this file are activated by this event.<\/li>\n\n\n\n<li>The bubble continues to rise from <strong>ImageGallery.js<\/strong> to <strong>App.js<\/strong>. Here, the <strong><em>changeTitle<\/em> <\/strong>function is activated by the arrival of the event bubble. Once the function finishes, the bubble continues to rise through the component tree.<\/li>\n\n\n\n<li>The event rises to <strong>index.js<\/strong>. No functions exist in this file to react to the event bubble. The bubble rises out of <strong>index.js<\/strong> and is released into the ether.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Build the Components<\/h2>\n\n\n\n<p>The basic container layout of the components will be something like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"688\" height=\"595\" src=\"https:\/\/www.shawntgray.com\/blog\/wp-content\/uploads\/2025\/06\/components.jpg\" alt=\"Basic Components Layout\" class=\"wp-image-56\" srcset=\"https:\/\/www.shawntgray.com\/blog\/wp-content\/uploads\/2025\/06\/components.jpg 688w, https:\/\/www.shawntgray.com\/blog\/wp-content\/uploads\/2025\/06\/components-300x259.jpg 300w\" sizes=\"auto, (max-width: 688px) 100vw, 688px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Start Small &amp; Build Up<\/h2>\n\n\n\n<p>With the basic roadmap laid out, we&#8217;ll start with the bottom-most component.<\/p>\n\n\n\n<p>In your <strong>src <\/strong>folder, create a new file named <strong>ImageCard.js<\/strong>, and add the following code:<\/p>\n\n\n\n<p><strong>ImageCard.js<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const ImageCard = (image) => {\n  return (\n    &lt;div className=\"image-card\">\n      &lt;img src={`${process.env.PUBLIC_URL}${image.src}`} alt={image.title} \/>\n      &lt;p>&lt;strong>{image.title}&lt;\/strong>&lt;\/p>\n    &lt;\/div>\n  );\n}\n\nexport default ImageCard;<\/code><\/pre>\n\n\n\n<p>Some notes about this code:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>This component inherits the <strong><em>image<\/em><\/strong> property from its parent component (which we will write next).<\/li>\n\n\n\n<li>Each attribute for the image tag is defined by variables, which must be contained within <strong>{<\/strong> and <strong>}<\/strong>.<\/li>\n\n\n\n<li>If an attribute is defined by multiple variables, enclose its definition within <strong>`<\/strong> tick marks. Each enclosed variable must be written as <strong>${ &#8230; }<\/strong>, like <strong><em>${process.env.PUBLIC_URL}<\/em><\/strong> included in the img&#8217;s src attribute.<\/li>\n\n\n\n<li><strong><em>process.env.PUBLIC_URL<\/em><\/strong> is a safe, convenient way to point to the app&#8217;s <strong>root <\/strong>folder without exposing the actual directory.<\/li>\n\n\n\n<li><strong><em>export default ImageCard<\/em><\/strong> allows other components to utilize it as its own <strong>&lt;ImageCard \/><\/strong> tag.<\/li>\n<\/ul>\n\n\n\n<p>Next, build the <strong>ImageGallery component<\/strong> by creating a new file in your <strong>src <\/strong>folder named <strong>ImageGallery.js<\/strong>. Add the following code:<\/p>\n\n\n\n<p><strong>ImageGallery.js<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import ImageCard from '.\/ImageCard';\n\nconst ImageGallery = (images) => {\n  return (\n    &lt;div className=\"gallery\">\n      {images.map((image, i) => (\n        &lt;ImageCard\n          key={i}\n          image={image}\n        \/>\n      ))}\n    &lt;\/div>\n  );\n}\n\nexport default ImageGallery;<\/code><\/pre>\n\n\n\n<p>Some notes on this code:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>First, we import the <strong>ImageCard <\/strong>component from the <strong>ImageCard.js<\/strong> file (the extension is not necessary for JavaScript files).<\/li>\n\n\n\n<li>The <strong>ImageGallery <\/strong>inherits the <strong><em>images <\/em><\/strong>property from its parent.<\/li>\n\n\n\n<li>Inside the <strong>gallery <\/strong>container, we create an <strong>ImageCard <\/strong>for each image included in the <strong><em>images<\/em><\/strong> property using the <strong><em>map <\/em><\/strong>loop function (more efficient than <strong><em>for <\/em><\/strong>or <strong><em>foreach <\/em><\/strong>loops).<\/li>\n\n\n\n<li>When using the <strong><em>map <\/em><\/strong>loop, each element it creates requires a unique key, which is why the<strong><em> i<\/em><\/strong> variable is included. The <strong><em>i<\/em><\/strong> variable is increased by 1 each time the <strong><em>map <\/em><\/strong>loop iterates.<\/li>\n\n\n\n<li>The<strong> ImageCard<\/strong>&#8216;s <strong><em>image <\/em><\/strong>attribute is set to the inherited <strong><em>image <\/em><\/strong>property, which contains the <em>key\/value<\/em> pairs you created in your <strong>images.json<\/strong> file back in <a href=\"https:\/\/www.shawntgray.com\/blog\/how-to-build-an-art-gallery-with-react-part-2-set-the-stage\/\" title=\"\">Part 2<\/a> (i.e. title, src, and categories).<\/li>\n<\/ul>\n\n\n\n<p>Next, in your <strong>src <\/strong>folder, update your <strong>App.js<\/strong> file:<\/p>\n\n\n\n<p><strong>App.js<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import '.\/App.css';\nimport ImageGallery from '.\/ImageGallery';\nimport data from '.\/images.json';\n\nfunction App() {\n  return (\n    &lt;div className='App'>\n      &lt;h1>Art Gallery&lt;\/h1>\n      &lt;ImageGallery\n        images={data}\n      \/>\n    &lt;\/div>\n  );\n}\n\nexport default ArtGallery;<\/code><\/pre>\n\n\n\n<p>The changes we made to this file:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We are now importing <strong>App.css<\/strong>, <strong>ImageGallery.js<\/strong>, and<strong> images.json<\/strong> &#8211; setting the JSON to the <em><strong>data<\/strong> <\/em>variable.<\/li>\n\n\n\n<li>Inside the App container, we added the <strong>&lt;ImageGallery \/><\/strong> tag and gave it an attribute <strong><em>images <\/em><\/strong>that contains the <strong>images.json<\/strong> data.<\/li>\n\n\n\n<li>The <strong>images.json<\/strong> data is sent down to <strong>ImageGallery<\/strong>, which parses the data into individual images, each of which is fed into the <strong>ImageCard <\/strong>component.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Update the Styles<\/h2>\n\n\n\n<p>Now that we have the basic components in place, let&#8217;s update the <strong>App.css<\/strong> file.<\/p>\n\n\n\n<p><strong>App.css<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.App {\n  text-align: center;\n  padding: 20px;\n  min-height: 100vh;\n}\n\n.gallery {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n  gap: 8px;\n  justify-items: center;\n}\n\n.image-card {\n  text-align: center;\n  max-width: 200px;\n}\n\n.image-card img {\n  max-width: 100%;\n  border-radius: 10px;\n  box-shadow: 0 4px 6px rgba(0, 0, 0, .1);\n  cursor: pointer;\n}<\/code><\/pre>\n\n\n\n<p>Remember, these styles are imported into <strong>App.js<\/strong>, which then feeds them into <strong>ImageGallery.js <\/strong>and <strong>ImageCard.js<\/strong>.<\/p>\n\n\n\n<p><strong>In the next part<\/strong>, we&#8217;ll add functionality to filter the images by clicking one or more category tag buttons. The app will then display only those images that contain the selected tag(s) in its category list as defined in your <strong>images.json<\/strong> file.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Add building block components to your React-based Art Gallery. Learn about how properties and actions flow from parent to child and back again.<\/p>\n","protected":false},"author":1,"featured_media":34,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[9],"tags":[],"class_list":["post-43","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-art-gallery"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.shawntgray.com\/blog\/wp-json\/wp\/v2\/posts\/43","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.shawntgray.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.shawntgray.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.shawntgray.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.shawntgray.com\/blog\/wp-json\/wp\/v2\/comments?post=43"}],"version-history":[{"count":2,"href":"https:\/\/www.shawntgray.com\/blog\/wp-json\/wp\/v2\/posts\/43\/revisions"}],"predecessor-version":[{"id":57,"href":"https:\/\/www.shawntgray.com\/blog\/wp-json\/wp\/v2\/posts\/43\/revisions\/57"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.shawntgray.com\/blog\/wp-json\/wp\/v2\/media\/34"}],"wp:attachment":[{"href":"https:\/\/www.shawntgray.com\/blog\/wp-json\/wp\/v2\/media?parent=43"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.shawntgray.com\/blog\/wp-json\/wp\/v2\/categories?post=43"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.shawntgray.com\/blog\/wp-json\/wp\/v2\/tags?post=43"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}