Simple Vegetarian Fajitas


Everybody loves this simple vegetarian fajitas recipe! This Tex Mex dinner is stuffed with large taste and exquisite colourful veggies. As two cookbook authors, fajitas is one thing we all know will at all times please a crowd, whether or not it’s served with tortillas or as fajita salad.

Simple Vegetarian Fajitas

Why we love this recipe

Right here’s a Tex Mex meal that’s at all times a success for us—and an effective way to infuse a great deal of veggies with out giving it a second thought. These Vegetarian Faijtas are so full flavored, they please everybody across the desk—even the meat lovers!

This fajita recipe stars our favourite fajita veggies, blackened within the oven and completed on the range, with pinto beans cooked in a flavorful fajita sauce. It’s so satisfying, you’ll promptly add it to your common rotation. We love serving it with tortillas or as fajita salad over greens, making it work for a lot of completely different diets!

Ideas for vegetarian fajitas

These vegetarian fajita recipe can come collectively in about half-hour. There’s a bit backwards and forwards between the cooking strategies, so listed below are a number of suggestions:

  • Brush up in your knife expertise. There’s fairly a little bit of veggies right here, so use your most effective knife expertise. Head to How you can Reduce a Pepper or How you can Reduce an Onion if you must brush up.
  • Broil the veggies first. Throwing the veggies in a broiler will get them to launch their liquid so that they’ll all slot in one sauté pan, and will get them frivolously charred.
  • Whereas the veggies broil, prep the beans. Make our home made fajita sauce and throw it in a skillet with the beans.
  • End the veggies in a skillet: Ending within the skillet will get them good and tender and that basic fajita skillet sizzle.
Vegetarian fajita recipe

Pinto beans add protein and taste

The important thing to vegetarian fajitas is discovering a stand in for meat that has nice taste and quantity of protein. We’ve used portobello mushrooms within the vegetable combine so as to add large meaty taste. However mushrooms don’t have protein, so on their very own they’re not very filling.

Pinto beans are good for vegetarian fajitas! They’re savory with a strong taste, they usually style unimaginable with home made fajita sauce. If you happen to favor, you need to use black beans as a substitute. Simply don’t skimp on the beans, since they’re the foremost protein supply.

Vegetarian fajitas
Use our fajita veggies for any kind of fajitas: vegetarian, shrimp or meat!

Home made fajita sauce is vital

We extremely advocate our home made fajita sauce for this recipe! It’s acquired an excellent zing from lime juice, seasoning from cumin and garlic powder, and a smoky physique from adobo sauce. Head over to Home made Fajita Sauce for the complete recipe!

A bit extra about chipotle peppers in adobo sauce: this ingredient is straightforward to seek out at most grocery shops close to the Mexican merchandise. All you do is use a spoon to take away adobo sauce from the can. Refrigerate or freeze the rest, and use it for extra adobo sauce recipes, like tortilla soup, enchilada sauce, or chipotle ranch dressing.

Vegetarian fajitas

Toppings for vegetarian fajitas

When you’ve made your veggies and pinto beans: let’s speak toppings! You should utilize any of your favourite fajita toppings right here. In truth: the toppings are the one factor that make this vegetarian fajita recipe! In any other case, all of the elements are vegan. So you may make this a vegan fajitas recipe with plant-based toppings. Listed below are our must-have fajitas toppings (what are yours?):

  • Pico de gallo: It provides simply the appropriate zing
  • Bitter cream: At all times. For vegan, substitute Cashew Cream
  • Recent cilantro: One other should! It’s the right distinction to the savory veggies
  • Guacamole: Non-compulsory — or use a mashed avocado
  • Cheese: Non-compulsory too! You’ll be able to add shredded cheddar, feta, queso fresco or cotija cheese: however the taste doesn’t want it! It’s extremely flavorful as is.
Vegetarian fajitas

Sides to serve

These vegetarian fajitas are implausible on their very own: most nights we don’t even make a facet to go along with them! (Reality.) However listed below are a number of simple concepts for what to serve with fajitas:

Extra fajitas recipes? Attempt these Shrimp Fajitas, Vegan Fajitas or Fajita Salad.

Dietary notes

This vegetarian fajita recipe is vegetarian, vegan, plant-based, dairy-free and gluten-free (with corn tortillas).

Steadily requested questions

What else can I exploit as an alternative to meat in vegetarian fajitas?

There are many scrumptious choices for vegetarian fajitas! Some widespread selections embody:

Portobello mushrooms: Their meaty texture and umami taste make them an excellent substitute for steak.

Tofu or tempeh: These soy-based proteins may be marinated and grilled for a satisfying chunk.

Black beans or lentils: These legumes add protein and fiber to your fajitas.

Can I make vegetarian fajitas forward of time?

Sure! You’ll be able to chop the greens and put together the seasonings prematurely. Retailer them individually within the fridge and cook dinner them simply earlier than serving. This makes vegetarian fajitas an excellent possibility for meal prepping or entertaining.

Can I grill vegetarian fajitas as a substitute of cooking them on the stovetop?

Completely! Grilling fajitas imparts a scrumptious smoky taste. Thread greens onto skewers or cook dinner them immediately on the grill grates over medium warmth.

Print

Vegetarian fajitas

Simple Vegetarian Fajitas


5 Stars 4 Stars 3 Stars 2 Stars 1 Star

4.6 from 17 opinions


  • Creator:
    Sonja Overhiser


  • Prep Time:
    10 minutes


  • Cook dinner Time:
    20 minutes


  • Whole Time:
    half-hour


  • Yield:
    4 to six servings


Save Recipe

Recipe Saved

(operate(){
operate initGrowMeSdk() {
if (!window.growMe) {
window.growMe = operate (e) {
window.growMe._.push(e);
}
window.growMe._ = [];
}
}
initGrowMeSdk();
window.growMe(operate() {
operate updateFavoriteButtonState(isFavorite) {
doc.querySelectorAll(‘.tasty-recipes-mediavine-button’).forEach(operate(el) {
el.querySelector(‘.tasty-recipes-saved’).fashion.show = isFavorite ? null : ‘none’;
el.querySelector(‘.tasty-recipes-not-saved’).fashion.show = isFavorite ? ‘none’ : null;
});
}
doc.querySelectorAll(‘.tasty-recipes-mediavine-button’).forEach(operate(el) {
el.addEventListener(‘click on’, operate() {
window.growMe.addBookmark();
});
});
window.growMe.on(‘isBookmarkedChanged’, operate(information) {
updateFavoriteButtonState(information.isBookmarked);
});
var isBookmarked = window.growMe.getIsBookmarked();
updateFavoriteButtonState(isBookmarked);
});
}())

Description

Everybody loves this simple vegetarian fajitas recipe! This Tex Mex dinner is stuffed with large taste and exquisite colourful veggies. As two cookbook authors, fajitas is one thing we all know will at all times please a crowd, whether or not it’s with tortillas or as fajita salad.


Components

  • 3 bell peppers (crimson, yellow, inexperienced)
  • 1 medium crimson onion
  • 2 portobello mushrooms
  • 4 tablespoons oil, divided
  • 1 ½ teaspoons kosher salt, divided
  • 3 cloves smashed garlic
  • 2 teaspoons cumin
  • 1 every teaspoon chili powder, garlic powder, and smoked paprika
  • ¼ teaspoon black pepper
  • 2 15-ounce cans pinto beans
  • 1 recipe Home made Fajita Sauce*
  • Small tortillas
  • For the toppings: Bitter cream, pico de gallo, contemporary cilantro, guacamole (optionally available), shredded cheese (optionally available)


Directions

  1. Broil the veggies: Preheat a broiler. Thinly slice the bell peppers and thinly slice the onion. Take away the stems and slice the portobello mushrooms. Place them in a bowl and toss with 2 tablespoons oil and 1 teaspoon kosher salt. Unfold the veggies on a parchment lined sheet pan. Place within the broiler and broil 8 to 10 minutes, stirring midway via, till softened and blackened on some edges. Then go proper to Step 3. 
  2. Cook dinner the beans: In the meantime in a skillet, whisk collectively the elements for the Home made Fajita Sauce. Drain and rinse the pinto beans and add them to the skillet. Warmth over medium warmth and cook dinner till warmed via and the sauce is thickened, about 6 to eight minutes. Style and stir in one other ¼ teaspoon kosher salt. 
  3. Saute the veggies: As soon as the veggies are performed within the broiler, end them on the range by heating the remaining 2 tablespoons oil in a forged iron skillet or giant non-stick skillet over medium excessive warmth. Add the broiled veggies, smashed garlic cloves, cumin, chili powder, garlic powder, smoked paprika, and the remaining ½ teaspoon kosher salt. Cook dinner for five minutes, stirring sometimes, till the veggies are tender. Take away and discard the garlic cloves earlier than serving.
  4. Heat the tortillas: Heat and char the tortillas by inserting them on an open fuel flame on medium for a number of seconds per facet, flipping with tongs, till they’re barely blackened and heat. (See How you can Heat Tortillas.)
  5. Serve: Place bowls of fajita veggies and pinto beans on the desk, with tortillas, bitter cream, pico de gallo, guacamole, and contemporary cilantro. Let every particular person add their desired toppings and serve. Leftovers retailer refrigerated for as much as 3 days.

Notes

*We extremely advocate this home made fajita sauce! Or you may substitute ½ cup retailer purchased sauce. For the fajita sauce, combine collectively 3 tablespoons contemporary lime juice, 1 tablespoon cumin, 1 to 2 tablespoons adobo sauce (from 1 can chipotle peppers in adobo sauce), 1 teaspoon garlic powder, 1 teaspoon kosher salt, and ¼ cup olive oil.

  • Class: Essential DIsh
  • Methodology: Stovetop
  • Delicacies: Tex Mex
  • Weight-reduction plan: Vegetarian

(operate(){
window.addEventListener( ‘message’, operate( occasion ){
if ( ( ‘https://nutrifox.com’ !== occasion.origin && ‘https://nutrifox.check’ !== occasion.origin ) ||
typeof occasion.information !== ‘string’ ) {
return;
}
var payload = JSON.parse( occasion.information );
change ( payload.occasion ) {
case ‘setHeight’:
var iframe = doc.getElementById( ‘nutrifox-label-‘ + payload.recipeId );
iframe.fashion.peak = payload.peak + ‘px’;
break;
}
} );
}());

window.trCommon={“minRating”:6,”ajaxurl”:”https://www.acouplecooks.com/wp-admin/admin-ajax.php”,”ratingNonce”:””,”postId”:94251};
window.TastyRecipes = window.TastyRecipes || {};

window.TastyRecipes.smoothScroll = {
init() {
window.addEventListener( ‘click on’, e => {
let anchor = e.goal;
if ( ! anchor.classList.comprises( ‘tasty-recipes-scrollto’ ) ) {
if ( anchor.tagName === ‘A’ ) {
return;
}

anchor = anchor.closest( ‘a’ );
if ( ! anchor || ! anchor.classList.comprises( ‘tasty-recipes-scrollto’ ) ) {
return;
}
}

const elementHref = anchor.getAttribute( ‘href’ );
if ( ! elementHref ) {
return;
}

e.preventDefault();
this.goToSelector( elementHref );
});
},
goToSelector( selector ) {
const component = doc.querySelector( selector );
if ( ! component ) {
return;
}
component.scrollIntoView( { conduct: ‘clean’ } );
historical past.pushState( {}, ”, selector );
}
};

(operate( callback ) {
if ( doc.readyState !== ‘loading’ ) {
callback();
} else {
window.addEventListener( ‘load’, callback );
}
})(() => {
window.TastyRecipes.smoothScroll.init();
});

window.TastyRecipes = window.TastyRecipes || {};
window.TastyRecipes.cookMode = {
wakeLockApi: false,
wakeLock: false,
cookModeSelector: ‘.tasty-recipes-cook-mode’,
init() {
if (“wakeLock” in navigator && “request” in navigator.wakeLock) {
this.wakeLockApi = navigator.wakeLock;
}

const cookModes = doc.querySelectorAll(this.cookModeSelector);

if (cookModes.size > 0) {
for (const cookMode of cookModes) {
if (this.wakeLockApi) {
cookMode.querySelector(‘enter[type=”checkbox”]’).addEventListener(“change”, occasion => {
this.checkboxChange(occasion.goal);
}, false);
} else {
cookMode.fashion.show = “none”;
}
}
}
},
checkboxChange(checkbox) {
if (checkbox.checked) {
this.lock();
} else {
this.unlock();
}
},
setCheckboxesState(state) {
const checkboxes = doc.querySelectorAll(this.cookModeSelector + ‘ enter[type=”checkbox”]’);
for (const checkbox of checkboxes) {
checkbox.checked = state;
}
},
async lock() {
attempt {
this.wakeLock = await this.wakeLockApi.request(“display screen”);
this.wakeLock.addEventListener(“launch”, () => {
this.wakeLock = false;
this.setCheckboxesState(false);
});
this.setCheckboxesState(true);
} catch (error) {
this.setCheckboxesState(false);
}
},
unlock() {
if (this.wakeLock) {
this.wakeLock.launch();
this.wakeLock = false;
}
this.setCheckboxesState(false);
}
};

(operate(callback) {
if (doc.readyState !== “loading”) {
callback();
} else {
doc.addEventListener(“DOMContentLoaded”, callback);
}
})(() => {
window.TastyRecipes.cookMode.init();
});

window.TastyRecipes = window.TastyRecipes || {};

window.TastyRecipes.staticTooltip = {
component: null,
tooltipElement: null,
deleting: false,
init( component ) {
if ( this.deleting ) {
return;
}
this.component = component;
this.buildElements();
},
destroy() {
if ( ! this.tooltipElement || this.deleting ) {
return;
}

this.deleting = true;
this.tooltipElement.classList.take away( ‘opened’ );

setTimeout( () => {
this.tooltipElement.take away();
this.deleting = false;
}, 500 );
},
buildElements() {
const tooltipElement = doc.createElement( ‘div’ );
tooltipElement.classList.add( ‘tasty-recipes-static-tooltip’);
tooltipElement.setAttribute( ‘id’, ‘tasty-recipes-tooltip’ );

const currentTooltipElement = doc.getElementById( ‘tasty-recipes-tooltip’ );
if ( currentTooltipElement ) {
doc.physique.replaceChild( tooltipElement, currentTooltipElement );
} else {
doc.physique.appendChild( tooltipElement );
}

this.tooltipElement = doc.getElementById( ‘tasty-recipes-tooltip’ );
},
present() {
if ( ! this.tooltipElement ) {
return;
}

const tooltipTop = this.component.getBoundingClientRect().high
+ window.scrollY
– 10 // 10px offset.
– this.tooltipElement.getBoundingClientRect().peak;
const tooltipLeft = this.component.getBoundingClientRect().left
– ( this.tooltipElement.getBoundingClientRect().width / 2 )
+ ( this.component.getBoundingClientRect().width / 2 ) – 1;
const posLeft = Math.max( 10, tooltipLeft );
this.maybeRemoveTail( posLeft !== tooltipLeft );

this.tooltipElement.setAttribute( ‘fashion’, ‘high:’ + tooltipTop + ‘px;left:’ + posLeft + ‘px;’ );
this.tooltipElement.classList.add( ‘opened’ );

},
maybeRemoveTail( removeTail ) {
if ( removeTail ) {
this.tooltipElement.classList.add( ‘tr-hide-tail’ );
} else {
this.tooltipElement.classList.take away( ‘tr-hide-tail’ );
}
},
changeMessage( message ) {
if ( ! this.tooltipElement ) {
return;
}
this.tooltipElement.innerHTML = message;
}
};

window.TastyRecipes.ajax = {
sendPostRequest( url, information, success, failure ) {
const xhr = new XMLHttpRequest();
xhr.open( ‘POST’, url, true );
xhr.ship( this.preparePostData( information ) );

xhr.onreadystatechange = () => {
if ( 4 !== xhr.readyState ) {
return;
}
if ( xhr.standing === 200 ) {
success( JSON.parse( xhr.responseText ) );
return;
}

failure( xhr );
};

xhr.onerror = () => {
failure( xhr );
};
},
preparePostData( information ) {
const formData = new FormData();

for ( const key in information ) {
formData.append( key, information[key] );
}
return formData;
},
};

window.TastyRecipes.scores = {
defaultRating: 0,
currentRatingPercentage: 100,
savingRating: false,
init( minRating ) {
this.minRating = minRating;

this.formWatchRating();
this.closeTooltipWhenClickOutside();
this.addBodyClassBasedOnSelectedRating();
this.backwardCompFormRatingPosition();
},
formWatchRating() {
const scores = doc.querySelectorAll(‘.tasty-recipes-no-ratings-buttons [data-rating]’);
if ( scores.size {
occasion.preventDefault();
this.defaultRating = occasion.goal.closest( ‘.checked’ ).dataset.score;
this.setCheckedStar( occasion.goal );
this.maybeSendRating( this.defaultRating, occasion.goal );
this.setRatingInForm( this.defaultRating );
} );
}
},
closeTooltipWhenClickOutside() {
window.addEventListener( ‘click on’, e => {
// Bailout (do not take away the tooltip) when the clicked component is a score star, or it is the tooltip itself.
if ( e.goal.closest( ‘.tasty-recipes-rating’ ) || e.goal.classList.comprises( ‘tasty-recipes-static-tooltip’ ) ) {
return;
}

window.TastyRecipes.staticTooltip.destroy();
} );
},
setRatingInForm( score ) {
const ratingInput = doc.querySelector( ‘#reply .tasty-recipes-rating[value=”‘ + rating + ‘”]’ );
if ( ! ratingInput ) {
return;
}
ratingInput.click on();
},
addBodyClassBasedOnSelectedRating() {
const ratingInputs = doc.querySelectorAll( ‘enter.tasty-recipes-rating’ );
if ( ! ratingInputs ) {
return;
}
for ( const ratingInput of ratingInputs ) {
ratingInput.addEventListener( ‘click on’, currentEvent => {
const selectedRating = currentEvent.goal.getAttribute( ‘worth’ );
this.handleBodyClassByRating( selectedRating );
this.toggleCommentTextareaRequired( selectedRating );
} );
}
},
handleBodyClassByRating( score ) {
if ( score < this.minRating ) {
doc.physique.classList.take away( 'tasty-recipes-selected-minimum-rating' );
return;
}
doc.physique.classList.add( 'tasty-recipes-selected-minimum-rating' );
},
toggleCommentTextareaRequired( score ) {
const commentTextarea = doc.getElementById( 'remark' );
if ( ! commentTextarea ) {
return;
}

if ( score {
window.TastyRecipes.staticTooltip.changeMessage( response.information.message );
window.TastyRecipes.staticTooltip.present();
this.updateAverageText( response.information, recipeCardElement );
this.maybeFillCommentForm( response.information );

// Disguise the tooltip after 5 seconds.
setTimeout( () => {
this.maybeResetTooltip( recipeCardElement, response.information, score );
}, 5000 );
},
() => {
this.resetTooltip( recipeCardElement );
}
);
},
updateAverageText( information, recipeCardElement ) {
if ( ! information.common ) {
return;
}
this.setRatingPercent( information );

if ( ! information.depend ) {
return;
}

const quickLink = doc.querySelector( ‘.tasty-recipes-rating-link’ );
if ( quickLink ) {
this.setTextInContainer( quickLink, information );
this.setPartialStar( quickLink );
}

const cardStars = recipeCardElement.querySelector( ‘.tasty-recipes-ratings-buttons’ );
cardStars.dataset.trDefaultRating = information.common;
this.setTextInContainer( recipeCardElement.querySelector( ‘.tasty-recipes-rating’ ), information );
},
setTextInContainer( container, information ) {
if ( ! container ) {
return;
}

if ( information.label ) {
const ratingLabelElement = container.querySelector( ‘.rating-label’ );
if ( ratingLabelElement ) {
ratingLabelElement.innerHTML = information.label;
}
return;
}

const averageElement = container.querySelector( ‘.common’ );
if ( averageElement ) {
averageElement.textContent = information.common;
}

const countElement = container.querySelector( ‘.depend’ );
if ( countElement ) {
countElement.textContent = information.depend;
}
},
setPartialStar( container ) {
const highestStar = container.querySelector( ‘[data-rating=”‘ + Math.ceil( this.defaultRating ) + ‘”]’ );
if ( highestStar ) {
highestStar.dataset.trClip = this.currentRatingPercentage;
}
},
setRatingPercent( information ) {
this.defaultRating = information.common.toFixed( 1 );
const elements = information.common.toFixed( 2 ).toString().break up( ‘.’ );
this.currentRatingPercentage = elements[1] ? elements[1] : 100;
if ( this.currentRatingPercentage === ’00’ ) {
this.currentRatingPercentage = 100;
}
},
setCheckedStar( goal ) {
const cardRatingContainer = goal.closest( ‘.tasty-recipes-ratings-buttons’ );
const selectedRatingElement = cardRatingContainer.querySelector( ‘[data-tr-checked]’ );
if ( selectedRatingElement ) {
delete selectedRatingElement.dataset.trChecked;
}

const thisStar = goal.closest( ‘.tasty-recipes-rating’ );
thisStar.dataset.trChecked = 1;
thisStar.querySelector( ‘[data-tr-clip]’ ).dataset.trClip = 100;
},
maybeFillCommentForm( information ) {
if ( ! information.remark || ! information.remark.content material ) {
return;
}

const commentForm = doc.querySelector( ‘#commentform’ );
if ( ! commentForm ) {
return;
}

const commentBox = commentForm.querySelector( ‘[name=comment]’ );
if ( ! commentBox || commentBox.worth ) {
return;
}

// Add remark particulars for modifying.
commentBox.innerHTML = information.remark.content material;
if ( information.remark.identify ) {
commentForm.querySelector( ‘[name=author]’ ).worth = information.remark.identify;
commentForm.querySelector( ‘[name=email]’ ).worth = information.remark.electronic mail;
}
},
maybeResetTooltip( recipeCardElement, information, score ) {
if ( this.savingRating === score ) {
this.resetTooltip( recipeCardElement, information );
}
},
resetTooltip( recipeCardElement, information ) {
window.TastyRecipes.staticTooltip.destroy();
this.savingRating = false;

// Reset the default score.
const cardRatingContainer = recipeCardElement.querySelector( ‘.tasty-recipes-ratings-buttons’ );
if ( cardRatingContainer ) {
this.defaultRating = ( information && information.common ) ? information.common.toFixed(1) : cardRatingContainer.dataset.trDefaultRating;
cardRatingContainer.dataset.trDefaultRating = this.defaultRating;

this.resetSelectedStar( cardRatingContainer, information );
}
},
resetSelectedStar( cardRatingContainer ) {
const selectedRatingElement = cardRatingContainer.querySelector( ‘[data-rating=”‘ + Math.ceil( this.defaultRating ) + ‘”]’ );
if ( selectedRatingElement ) {
selectedRatingElement.querySelector( ‘[data-tr-clip]’ ).dataset.trClip = this.currentRatingPercentage;
selectedRatingElement.parentNode.dataset.trChecked = 1;
}

const previousSelectedElement= cardRatingContainer.querySelector( ‘[data-tr-checked]’ );
if ( previousSelectedElement ) {
const currentSelectedRating = previousSelectedElement.querySelector(‘[data-rating]’);
if ( currentSelectedRating !== selectedRatingElement ) {
delete previousSelectedElement.dataset.trChecked;
}
}
},
backwardCompFormRatingPosition() {
const ratingsButtons = doc.querySelector( ‘#reply .tasty-recipes-ratings-buttons, #tasty-recipes-comment-rating .tasty-recipes-ratings-buttons’ );
if ( ! ratingsButtons ) {
return;
}
const ratingsButtonsStyles = window.getComputedStyle(ratingsButtons);
if ( ! ratingsButtonsStyles.show.contains( ‘flex’ ) ) {
ratingsButtons.fashion.path = ‘rtl’;
}

if ( typeof tastyRecipesRating !== ‘undefined’ ) {
// Choose the score that was beforehand chosen in admin.
ratingsButtons.querySelector( ‘.tasty-recipes-rating[value=”‘ + tastyRecipesRating + ‘”]’ ).checked = true;
}

const ratingSpans = ratingsButtons.querySelectorAll( ‘.tasty-recipes-rating’ );
for (const ratingSpan of ratingSpans) {
ratingSpan.addEventListener( ‘click on’, occasion => {
if ( ratingSpan === occasion.goal ) {
return;
}
ratingSpan.previousElementSibling.click on();
} );
}
}
};

(operate(callback) {
if (doc.readyState !== “loading”) {
callback();
} else {
window.addEventListener( ‘load’, callback );
}
})(() => {
window.TastyRecipes.scores.init( window.trCommon ? window.trCommon.minRating : 4 );
});





Supply hyperlink

We will be happy to hear your thoughts

Leave a reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Easy Click Express
Logo
Compare items
  • Total (0)
Compare
0
Shopping cart