Ready to get started?

Join Adocasts Plus for $8/mo, or sign into an existing Adocasts Plus account, to get access to all of our lessons.

robot mascot smiling

Completing Our AppLayout & Navigation Bar

In This Lesson

We'll finish setting up our application's layout shell and navigation bar.

Created by
@tomgobich
Published

Chapters

00:00 - What's Our End Goal?
01:13 - Setting Up Our Header & Main Elements
03:12 - Creating Our Header's Navigation
07:05 - Creating the Mobile Navigation Sheet
10:45 - Applying Our Navigation Component to the AppLayout
11:40 - Adding Our User Menu Dropdown
15:40 - Inspecting Our Work

Final Layout Components

Join the Discussion 2 comments

Create a free account to join in on the discussion
  1. @aaron-ford

    I have been going through trying to clean up my console errors. I have an error for 'Extraneous non-props attributes (user, messages) were passed to component but could not be automatically inherited because component renders fragment or text root nodes'. I ran your code and see the error exists there too. How do we fix this? I also get Invalid prop: type check failed for prop "href". Expected String with value "null", got Null for the pagination links. I tried modifying the components to allow null for the href, but that didn't work. Does the pagination dto npm package need to be modified?

    Update: I wrapped the nav and sheet into a parent div and that seems to fix the hydration issues. Either that, or listing user and messages in the props, but since the nav doesn't directly use them, I went with wrapping it in a div.

    1
    1. Responding to aaron-ford
      @tomgobich

      Hey Aaron! Ah yes, sorry about that! That warning is in regards to Vue's fallthrough attributes, ie: values passed to the to the component that aren't declared as props. When the component has a single root node, Vue will default to just passing that undeclared prop as an attribute. However, when there are multiple root nodes, as I accidentally did with the Navigation.vue component Vue isn't sure what to do with them and issues that warning.

      I agree, just wrapping the component template contents in a div would be the preferred solution here!

      As for the pagination links, is this in regard to the next/prev links? If so, I think you should be able to do something like the below. Allow the href prop to be null, then use a v-if to determine if the link should be utilized. Or, you could use nullish coalesce when passing in the prop to default to an empty string. The next/prev links, coming directly from the paginator, will be null if there isn't a next or previous page to go to.

      So something like this using nullish coalesce:

      <PaginationPrev :href="lessons.meta.previousPageUrl ?? ''" />
      Copied!

      Or this inside the next/prev components:

      <script setup lang="ts">
      import { ChevronLeft } from 'lucide-vue-next'
      import { PaginationPrev, type PaginationPrevProps } from 'radix-vue'
      import { type HTMLAttributes, computed } from 'vue'
      import { Button } from '~/components/ui/button'
      import { cn } from '~/lib/utils'
      
      const props = withDefaults(
        defineProps<PaginationPrevProps & { 
          class?: HTMLAttributes['class']; 
          href: string | null
        }>(),
        {
          asChild: true,
        }
      )
      
      const delegatedProps = computed(() => {
        const { class: _, ...delegated } = props
      
        return delegated
      })
      </script>
      
      <template>
        <PaginationPrev v-bind="delegatedProps">
          <Button :class="cn('w-8 h-8 p-0', props.class)" variant="outline">
            <Link v-if="href" :href="href">
              <ChevronLeft class="w-4 h-4" />
            </Link>
            <template v-else>
              <ChevronLeft class="w-4 h-4" />
            </template>
          </Button>
        </PaginationPrev>
      </template>
      
      Copied!

      0