{"id":27351816,"url":"https://github.com/enkidevs/seapig","last_synced_at":"2025-04-12T20:35:08.081Z","repository":{"id":22412589,"uuid":"96175975","full_name":"enkidevs/seapig","owner":"enkidevs","description":" 🌊🐷 Utility for generalized composition of React components","archived":false,"fork":false,"pushed_at":"2023-03-06T04:56:43.000Z","size":4917,"stargazers_count":286,"open_issues_count":20,"forks_count":21,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-18T03:41:21.282Z","etag":null,"topics":["composition","compound-components","javascript","props","react","react-component","reactjs","schema"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/enkidevs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-07-04T04:43:36.000Z","updated_at":"2025-03-13T20:05:44.000Z","dependencies_parsed_at":"2024-06-21T17:38:38.133Z","dependency_job_id":"661394c2-e518-4b7f-9a42-91c88b1f4234","html_url":"https://github.com/enkidevs/seapig","commit_stats":{"total_commits":524,"total_committers":12,"mean_commits":"43.666666666666664","dds":"0.46564885496183206","last_synced_commit":"cad65f753699305aaa0cec8add7b9a3fe87b215a"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enkidevs%2Fseapig","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enkidevs%2Fseapig/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enkidevs%2Fseapig/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enkidevs%2Fseapig/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/enkidevs","download_url":"https://codeload.github.com/enkidevs/seapig/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248631193,"owners_count":21136549,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["composition","compound-components","javascript","props","react","react-component","reactjs","schema"],"created_at":"2025-04-12T20:35:07.546Z","updated_at":"2025-04-12T20:35:08.066Z","avatar_url":"https://github.com/enkidevs.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\t\u003ca href=\"https://www.npmjs.org/package/seapig\"\u003e\n  \t\u003cimg src=\"https://media.giphy.com/media/X2C5xcQLdVGda/giphy.gif\" width=\"300\" height=\"300\" alt=\"seapig\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n# seapig 🌊🐷\n\n[Utility for generalized composition of React components](https://www.npmjs.org/package/seapig)\n\nSea pig stands for (**C**hildren **P**rops **I**nternal **G**etter), except the `C` is spelled phonetically.\n\n## Table of Contents\n\n-   [What does Seapig do?](#whatdoesseapigdo)\n-   [Install](#install)\n-   [Example](#example)\n-   [Walkthrough](#workthrough)\n-   [API](#api)\n-   [License](#license)\n\n## \u003ca name=\"whatdoesseapigdo\"\u003eWhat does Seapig do?\u003c/a\u003e\n\n\u003e `seapig`. `seapig`. Does whatever a `seapig` does!\n\n`seapig` is here to help you compose your React Components in a consistent and flexible manner. It provides a mechanism to organize components that form into a single idea without limiting the types of components you can use.\n\n\u003e “A way to give rendering back to the user”\n\n`seapig` eliminates configuration in favor of composition.\n\nIt abstracts component composition for you, allowing you to decouple *how* components are combined from *which* components they are. It enables you to generalize (and optionally enforce) the structure and behavior of a group of components but leaves out any restrictions on which exact components you must use.\n\n```jsx\n\u003cHeader\u003e\n  \u003ch1 right\u003e 🌊🐷\u003c/h1\u003e\n  \u003ch1 left\u003eSeapig\u003c/h1\u003e\n\u003c/Header\u003e\n```\n\nWould render something like ([play with the example](https://codepen.io/nem035/pen/QqrpgX?editors=1010)):\n\n![seapig example](https://image.ibb.co/bNjF1G/Screen_Shot_2017_10_10_at_1_32_52_AM.png)\n\n`seapig` shines the most when used to create components whose state is too complex to be held in a single component yet not global enough to be represented at the application level.\n\nUsing this pattern in React allows us to have the rendering control with the [minimal api surface area](https://www.youtube.com/watch?v=4anAwXYqLG8) without the need for configuration props for all state and data variations that our component can have.\n\nThis is where compound components come to the rescue.\n\nA compound component encloses the state and behavior for a group of components but still gives the rendering control for its variable parts back to the external user. When using it, you only need to worry about the actual parts that are different.\n\nA concrete example of a compound component on the Web today is the `\u003cselect\u003e` element. It allows us to externally specify its rendering structure with `\u003coption\u003e` and `\u003coptiongroup\u003e` elements but hides away all the complexity of rendering those in a certain way or handling/delegating events.\n\n\u003e If you're interested in more examples of compound components, [Ryan Florence](https://twitter.com/ryanflorence) has a [great talk](https://www.youtube.com/watch?v=hEGg-3pIHlE) on this topic.\n\nGood ol' `seapig` twists the concept of compound components a bit by using designated props to determine distinct parts of our component group, rather than enforcing usage of specific components. Furthermore, it can restrict a consistent shape of our structure by enforcing [**Rendering Order**](#RenderingOrder) and [**Child Presence**](#ChildPresence) using a schema object.\n\nImagine if `\u003cselect\u003e` allowed you to pass anything as its option but still handled all the logic and things like event propagation?\n\nWouldn't this be cool?\n\n```html\n\u003cselect\u003e\n  \u003cspan option\u003eOption 1\u003c/span\u003e\n  \u003ch3 option\u003eOption 2\u003c/span\u003e\n  \u003cdiv option className=\"capitalize\"\u003eOption 3\u003c/div\u003e\n\u003c/select\u003e\n```\n\n### \u003ca name=\"RenderingOrder\"\u003eRendering Order\u003c/a\u003e\n\nComponents that use `seapig` render their children into special placeholders determined by the schema. This means that the rendering shape can be enforced internally, allowing us to pass the children into a `seapig` component in any order.\n\n```jsx\n/* `MyCoolSidebar` and `Content` are always rendered in the same order no matter what order they are passed in */\n\n/* This one would render the same */\n\u003cMain\u003e\n  \u003cMyCoolSidebar sidebar /\u003e\n  \u003cContent content\u003e\n    \u003ch2\u003eHello `seapig`!\u003c/h2\u003e\n  \u003c/Content\u003e\n\u003c/Main\u003e\n\n/* as this one */\n\u003cMain\u003e\n  \u003cContent content\u003e\n    \u003ch2\u003eHello `seapig`!\u003c/h2\u003e\n  \u003c/Content\u003e\n  \u003cMyCoolSidebar sidebar /\u003e\n\u003c/Main\u003e\n\n/* Corresponding seapig component example */\nimport seapig, { OPTIONAL, REQUIRED } from 'seapig'\n\nconst Main = props =\u003e {\n  const {\n    sidebarChildren, // array of children with the `sidebar` prop\n    contentChildren  // array of children with the `content` prop\n  } = seapig(props.children, { // schema object\n    sidebar: OPTIONAL, // sidebar is optional\n    content: REQUIRED  // content is required\n  })\n\n  // rendering order is always the same\n  return (\n    \u003cdiv\u003e\n      {sidebarChildren.length \u0026\u0026 \u003caside\u003e{sidebarChildren}\u003c/aside\u003e}\n      \u003csection\u003e{content}\u003c/section\u003e\n    \u003c/div\u003e\n  )\n}\n```\n### \u003ca name=\"ChildPresence\"\u003eChild presence\u003c/a\u003e\n\nA `seapig` component ensures that all children match the provided schema.\n\nTo reuse `\u003cMain\u003e` from above as an example, if we didn't pass any children with the `'content'` prop, `seapig` would throw:\n\n```jsx\n// The code below would throw a \"Must have at least 1 `content` element\" error\n\u003cMain\u003e\n  \u003cMyCoolSidebar sidebar /\u003e\n\u003c/Main\u003e\n```\n\nThe `seapig` also accumulates any unidentified children into the `rest` array.\n\n```jsx\nimport seapig, { OPTIONAL, REQUIRED } from 'seapig'\n\nconst Main = props =\u003e {\n  const {\n    sidebarChildren,\n    contentChildren,\n    rest, // all children not matching `sidebar` and `content` are in this array\n  } = seapig(props.children, {\n    sidebar: OPTIONAL,\n    content: REQUIRED\n  })\n\n  return (\n    \u003cdiv\u003e\n      {sidebarChildren.length \u0026\u0026 \u003caside\u003e{sidebarChildren}\u003c/aside\u003e}\n      \u003csection\u003e{content}\u003c/section\u003e\n      {rest} {/* passing rest of the children */}\n    \u003c/div\u003e\n  )\n}\n\n/* `rest` would contain the bottom section */\n\u003cMain\u003e\n  \u003cContent content\u003e\n    \u003ch2\u003eHello `seapig`!\u003c/h2\u003e\n  \u003c/Content\u003e\n  \u003cMyCoolSidebar sidebar /\u003e\n  \u003csection\u003eI would be in the `rest` array\u003c/section\u003e\n\u003c/Main\u003e\n```\n\nIn fact, `OPTIONAL` and `REQUIRED`, along with their plural counterparts `OPTIONALS` and `REQUIREDS`, are just helpful schema constants:\n\n```jsx\nconst OPTIONAL = { // can have one\n  min: 0,\n  max: 1\n}\nconst OPTIONALS = { // can have any\n  min: 0\n}\nconst REQUIRED = { // must have one\n  min: 1,\n  max: 1\n}\nconst REQUIREDS = { // must have at least one\n  min: 1\n}\n```\n\n`seapig` allows us to pass custom `min` and `max`, both inclusive, values as well:\n\n```jsx\nimport seapig from 'seapig'\n\nconst Main = props =\u003e {\n  const {\n    buttonChildren\n  } = seapig(props.children, {\n    button: { // custom button schema values\n      min: 2,\n      max: 5\n    }\n  })\n\n  return (\n    \u003cdiv\u003e\n      \u003ch1\u003eI can have between 2 to 5 buttons\u003c/h1\u003e\n      {buttonChildren}\n    \u003c/div\u003e\n  )\n}\n\n/* Now we must have between 2 and 5 buttons */\n\u003cMain\u003e\n  \u003cButton button\u003eFirst\u003c/Button\u003e\n  \u003ca button\u003eSecond\u003c/a\u003e\n  \u003cdiv button\u003eThird\u003c/div\u003e\n\u003c/Main\u003e\n```\n\n## \u003ca name=\"install\"\u003eInstall\u003c/a\u003e\n\n```bash\nnpm install seapig --save\n\n# or\n\nyarn add seapig\n```\n\n## \u003ca name=\"example\"\u003eExample\u003c/a\u003e\n\n### `seapig` button with a required label and an optional icon:\n\n```jsx\nimport React, { Component } from 'react'\nimport seapig, { OPTIONAL, REQUIRED } from 'seapig'\n\n/* Button with a required label and an optional icon */\nclass Button extends Component {\n  render() {\n    const {\n      iconChildren,\n      labelChildren\n    } = seapig(this.props.children, {\n      icon: OPTIONAL,\n      label: REQUIRED\n    })\n\n    return (\n      \u003cbutton\u003e\n        {iconChildren.length \u0026\u0026 \u003cspan className=\"pull-left\"\u003e{iconChildren}\u003c/span\u003e}\n        {labelChildren}\n      \u003c/button\u003e\n    )\n  }\n}\n\n/* usage of the seapig Button */\nimport React, { Component } from 'react'\nimport Button from './Button'\n\nclass Form extends Component {\n  render() {\n    return (\n      \u003cform\u003e\n        {/* other form components */}\n        \u003cButton\u003e\n          \u003ci icon className=\"fa fa-upload\" /\u003e\n          \u003cspan label\u003eSubmit\u003c/span\u003e\n        \u003c/Button\u003e\n      \u003c/form\u003e\n    )\n  }\n}\n```\n\n## \u003ca name=\"workthrough\"\u003eWalkthrough\u003c/a\u003e\n\nTo demonstrate the problem that `seapig` solves, let's see how we can design the API of a header component.\n\nLet's say we came up with an initial idea that the header will show a brand image to the left and a menu right after it.\n\n```jsx\nconst Header = () =\u003e (\n  \u003cheader\u003e\n    \u003cimg className=\"brand\" src=\"www.mycoolsite.com/static/brand.jpg\" /\u003e\n    \u003cul className=\"menu\"\u003e\n      \u003cli\u003eHome\u003c/li\u003e\n      \u003cli\u003eAbout\u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/header\u003e\n)\n```\n\nOk simple enough. Now let's consider a few potential updates we might need to make this component.\n\nLet's imagine a requirement comes in that each page has to render a custom menu item that is unique to it, maybe some icon designated for that portion of our site that may or may not be an anchor as well.\n\nSure, not a problem, instead of us worrying about element types and icons for each page, let's just allow them to pass that in. Of course we don't want to render blank items if nothing is provided:\n\n```jsx\nconst Header = ({ PageIcon }) =\u003e (\n  \u003cheader\u003e\n    \u003cimg className=\"brand\" src=\"www.mycoolsite.com/static/brand.jpg\" /\u003e\n    \u003cul className=\"menu\"\u003e\n      \u003cli\u003eHome\u003c/li\u003e\n      \u003cli\u003eAbout\u003c/li\u003e\n      {PageIcon \u0026\u0026 \u003cli\u003e{PageIcon}\u003c/li\u003e}\n    \u003c/ul\u003e\n  \u003c/header\u003e\n)\n```\n\nNow let's use it:\n\n```jsx\n\u003cHeader PageIcon={\u003cFontAwesome name=\"star\" /\u003e} /\u003e\n{/* or */}\n\u003cHeader PageIcon={\u003ca href=\"/settings\"\u003e\u003cFontAwesome name=\"cog\" /\u003e\u003c/a\u003e} /\u003e\n```\n\nAlright, things are getting a bit messy but still manageable.\n\nHeaders usually show user info, assuming the user is authenticated.\n\n```jsx\nconst Header = ({ PageIcon, authenticated }) =\u003e (\n  \u003cheader\u003e\n    \u003cimg className=\"brand\" src=\"www.mycoolsite.com/static/brand.jpg\" /\u003e\n    \u003cul className=\"menu\"\u003e\n      \u003cli\u003eHome\u003c/li\u003e\n      \u003cli\u003eAbout\u003c/li\u003e\n      {PageIcon \u0026\u0026 \u003cli\u003e{PageIcon}\u003c/li\u003e}\n    \u003c/ul\u003e\n    {authenticated \u0026\u0026 \u003cUserInfo /\u003e}\n  \u003c/header\u003e\n)\n```\n\nFeels somewhat clunky but not terrible.\n\n```jsx\n\u003cHeader\n  PageIcon={\u003cFontAwesome name=\"star\" /\u003e}\n  authenticated={user.isLoggedIn \u0026\u0026 user.hasPermission}\n/\u003e\n```\n\nPhew, done. A few days go by and users now start complaining about having to navigate to `/search` to explore your site and want a search bar directly in the header. Ok, we can add another flag. Additionally there isn't a point to storing this search state within the header when other parts of the app will need it so let's take that into account as well.\n\n```jsx\nconst Header = ({ PageIcon, authenticated, showSearch, searchTerm, onSearch }) =\u003e (\n  \u003cheader\u003e\n    \u003cimg className=\"brand\" src=\"www.mycoolsite.com/static/brand.jpg\" /\u003e\n    \u003cul className=\"menu\"\u003e\n      \u003cli\u003eHome\u003c/li\u003e\n      \u003cli\u003eAbout\u003c/li\u003e\n      {PageIcon \u0026\u0026 \u003cli\u003e{PageIcon}\u003c/li\u003e}\n    \u003c/ul\u003e\n    {authenticated \u0026\u0026 \u003cUserInfo /\u003e}\n    {showSearch \u0026\u0026 (\n      \u003cinput type=\"search\" value={searchTerm} onChange={onSearch} /\u003e\n    )}\n  \u003c/header\u003e\n)\n```\n\nDid that mess things up?\n\n```jsx\n\u003cHeader\n  PageIcon={\u003cFontAwesome name=\"star\" /\u003e}\n  authenticated={user.isLoggedIn \u0026\u0026 user.hasPermission}\n  showSearch\n  searchTerm={this.state.searchTerm}\n  onSearch={this.handleSearch}\n/\u003e\n```\n\nYou can see where this is going.\n\n- What if we want the menu to sometimes be an `ol` rather than an `ul`? Theoretically, this header could be used in multiple sites. Surely we'll need to figure out how to generalize the hardcoded menu item texts for each site. Maybe a string array of items?\n- What if those other sites behave the same but will never need special icons?\n- What if the `\u003cinput\u003e` is sometimes only a numeric search? Pass a prop?\n- What if in the future we want other pages to customize the order of the internal components of `\u003cHeader\u003e`, maybe show the `input` before the `ul` menu?\n\nThis is where `seapig` comes in.\n\nLet's look at how a `seapig` `Header` API would look like in each of these case, we'll deal with implementation later.\n\n```jsx\n\u003cHeader\u003e\n  \u003cimg brand className=\"brand\" src=\"www.mycoolsite.com/static/brand.jpg\" /\u003e\n  \u003cul menu className=\"menu\"\u003e\n    \u003cli\u003eHome\u003c/li\u003e\n    \u003cli\u003eAbout\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/Header\u003e\n```\n\nThat's it. We mark our `img` and `ul` as the `brand` and `menu` and this satisfies all the requirements of our header for this particular page.\n\nWhat about the custom icon per page? We can just render it on each page directly:\n\n```jsx\n\u003cHeader\u003e\n  \u003cimg brand className=\"brand\" src=\"www.mycoolsite.com/static/brand.jpg\" /\u003e\n  \u003cul menu className=\"menu\"\u003e\n    \u003cli\u003eHome\u003c/li\u003e\n    \u003cli\u003eAbout\u003c/li\u003e\n    \u003cli\u003e\u003cFontAwesome name=\"star\" /\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/Header\u003e\n```\n\nOk, what about auth data? Well we could just mark those as well. We also want to pass the `authenticated` flag to the header so it knows which items to show or not depending on the `authentication` state.\n\n```jsx\n\u003cHeader authenticated={authenticated}\u003e\n  \u003cimg brand className=\"brand\" src=\"www.mycoolsite.com/static/brand.jpg\" /\u003e\n  \u003cul menu className=\"menu\"\u003e\n    \u003cli\u003eHome\u003c/li\u003e\n    \u003cli\u003eAbout\u003c/li\u003e\n    \u003cli\u003e\u003cFontAwesome name=\"star\" /\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n  \u003cUserInfo auth\u003e {/* Mark restricted menu items */} \u003c/UserInfo\u003e\n\u003c/Header\u003e\n```\n\nWhat about search?\n\n```jsx\n\u003cHeader authenticated={authenticated}\u003e\n  \u003cimg brand className=\"brand\" src=\"www.mycoolsite.com/static/brand.jpg\" /\u003e\n  \u003cul menu className=\"menu\"\u003e\n    \u003cli\u003eHome\u003c/li\u003e\n    \u003cli\u003eAbout\u003c/li\u003e\n    \u003cli\u003e\u003cFontAwesome name=\"star\" /\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n  \u003cUserInfo auth /\u003e\n  \u003cinput search type=\"search\" value={this.state.searchTerm} onChange={this.handleSearch} /\u003e\n\u003c/Header\u003e\n```\n\nWanna disable the search input? Just do it.\n\n```jsx\n\u003cHeader authenticated={authenticated}\u003e\n  \u003cimg brand className=\"brand\" src=\"www.mycoolsite.com/static/brand.jpg\" /\u003e\n  \u003cul menu className=\"menu\"\u003e\n    \u003cli\u003eHome\u003c/li\u003e\n    \u003cli\u003eAbout\u003c/li\u003e\n    \u003cli\u003e\u003cFontAwesome name=\"star\" /\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n  \u003cUserInfo auth /\u003e\n  \u003cinput search type=\"search\" disabled={this.state.isSearching} value={this.state.searchTerm} onChange={this.handleSearch} /\u003e\n\u003c/Header\u003e\n```\n\nWhat if the `\u003cinput\u003e` is sometimes only a numeric search? We can update the `type` property.\n\n```jsx\n\u003cHeader authenticated={authenticated}\u003e\n  \u003cimg brand className=\"brand\" src=\"www.mycoolsite.com/static/brand.jpg\" /\u003e\n  \u003cul menu className=\"menu\"\u003e\n    \u003cli\u003eHome\u003c/li\u003e\n    \u003cli\u003eAbout\u003c/li\u003e\n    \u003cli\u003e\u003cFontAwesome name=\"star\" /\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n  \u003cUserInfo auth /\u003e\n  \u003cinput search type=\"number\" disabled={this.state.isSearching} value={this.state.searchTerm} onChange={this.handleSearch} /\u003e\n\u003c/Header\u003e\n```\n\nWhat if the menu is an `ol`? We can change the tag.\n\n```jsx\n\u003cHeader authenticated={authenticated}\u003e\n  \u003cimg brand className=\"brand\" src=\"www.mycoolsite.com/static/brand.jpg\" /\u003e\n  \u003col menu className=\"menu\"\u003e\n    \u003cli\u003eHome\u003c/li\u003e\n    \u003cli\u003eAbout\u003c/li\u003e\n    \u003cli\u003e\u003cFontAwesome name=\"star\" /\u003e\u003c/li\u003e\n  \u003c/ol\u003e\n  \u003cUserInfo auth /\u003e\n  \u003cinput search type=\"search\" disabled={this.state.isSearching} value={this.state.searchTerm} onChange={this.handleSearch} /\u003e\n\u003c/Header\u003e\n```\n\nChange what the items say?\n\n```jsx\n\u003cHeader authenticated={authenticated}\u003e\n  \u003cimg brand className=\"brand\" src=\"www.mycoolsite.com/static/brand.jpg\" /\u003e\n  \u003cul menu className=\"menu\"\u003e\n    \u003cli\u003e🏠\u003c/li\u003e\n    \u003cli\u003eℹ️\u003c/li\u003e\n    \u003cli\u003e\u003cFontAwesome name=\"star\" /\u003e\u003c/li\u003e\n  \u003c/ul\u003e\n  \u003cUserInfo auth /\u003e\n  \u003cinput search type=\"search\" disabled={this.state.isSearching} value={this.state.searchTerm} onChange={this.handleSearch} /\u003e\n\u003c/Header\u003e\n```\n\nAny specific rendering change we need to make to one of its children doesn't need to affect the `\u003cHeader\u003e` component itself. `seapig` allows you to use whatever component you want.\n\nOnce you determine a consistent set of logic for your site or groups of pages, you can wrap that logic into a specific `seapig` header component and be done with it. Any future requirements to any other system that uses the header won't affect you and you won't affect them.\n\nSo how does the actual implementation of `Header` look like?\n\n```jsx\nconst Header = ({ children, authenticated }) =\u003e {\n  const {\n    brandChildren,\n    menuChildren,\n    authChildren\n    searchChildren,\n  } = seapig(children, {\n    brand: REQUIRED,\n    menu: REQUIRED,\n    auth: OPTIONAL\n    search: OPTIONAL,\n  });\n\n  return (\n    \u003cheader\u003e\n      {brandChildren}\n      {menuChildren}\n      {authenticated \u0026\u0026 authChildren}\n      {searchChildren}\n    \u003c/header\u003e\n  )\n}\n```\n\nFor a more advanced use case, `seapig` works great with [`React.cloneElement`](https://reactjs.org/docs/react-api.html#cloneelement).\n\nLet's say we want to have all `auth` children add a class `'authenticated-item'` when present in the header.\n\n```jsx\nconst Header = ({ children, authenticated }) =\u003e {\n  const {\n    brandChildren,\n    menuChildren,\n    authChildren\n    searchChildren,\n  } = seapig(children, {\n    brand: REQUIRED,\n    menu: REQUIRED,\n    auth: OPTIONAL\n    search: OPTIONAL,\n  });\n\n  return (\n    \u003cheader\u003e\n      {brandChildren}\n      {menuChildren}\n      {authenticated \u0026\u0026 authChildren.map(\n        child =\u003e React.cloneElement(child, {\n          className: `${child.props.className} authenticated-item`\n        })\n      )}\n      {searchChildren}\n    \u003c/header\u003e\n  )\n}\n```\n## \u003ca name=\"api\"\u003eAPI\u003c/a\u003e\n\n```jsx\nconst {\n  iconChildren,\n  iconsChildren,\n  labelChildren,\n  labelsChildren,\n  imageChildren,\n  rest, // array of children not matching any of the schema props\n} = seapig(children, {\n  icon: OPTIONAL, // use OPTIONAL to specify only one optional child prop\n  icons: OPTIONALS, // use OPTIONALS to specify any amount of optional child props\n  label: REQUIRED, // use REQUIRED to specify only one required child prop,\n  labels: REQUIREDS, // use REQUIREDS to specify at least one required child prop,\n  image: { // pass an object with `min` and/or `max`\n    min: 1, // default is `0` if only `max` is specified\n    max: 2  // default is `Infinity` if only `min` is specified\n  }\n})\n```\n\n## \u003ca name=\"license\"\u003eLicense\u003c/a\u003e\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenkidevs%2Fseapig","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fenkidevs%2Fseapig","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenkidevs%2Fseapig/lists"}