• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Step-by-Step Guide to building a custom XML sitemap generator block for Gutenberg using Vue micro-frontends

Step-by-Step Guide to building a custom XML sitemap generator block for Gutenberg using Vue micro-frontends

Project Setup and Dependencies

This guide details the creation of a custom Gutenberg block that generates an XML sitemap. We’ll leverage Vue.js for the block’s editor interface, treating it as a micro-frontend within the WordPress ecosystem. This approach offers a modern, component-based development experience for the block’s UI.

First, ensure you have a local WordPress development environment set up. We’ll use Composer for PHP dependencies and npm/yarn for JavaScript. Create a new plugin directory, for instance, wp-content/plugins/custom-sitemap-generator.

Initialize your project with a composer.json and package.json:

cd wp-content/plugins/custom-sitemap-generator
composer init
npm init -y

Next, install necessary PHP packages for WordPress plugin development and Vue.js tooling. We’ll use wp-scripts for compiling our Vue assets.

npm install @wordpress/scripts vue vue-loader vue-template-compiler --save-dev
composer require --dev dealerdirect/phpcodesniffer wp-coding-standards/wpcs

Configure composer.json to include WordPress Coding Standards for PHP linting.

{
    "name": "your-vendor/custom-sitemap-generator",
    "description": "Custom XML Sitemap Generator Gutenberg Block",
    "type": "wordpress-plugin",
    "license": "GPL-2.0-or-later",
    "authors": [
        {
            "name": "Your Name",
            "email": "[email protected]"
        }
    ],
    "require": {
        "php": ">=7.4",
        "dealerdirect/phpcodesniffer": "^3.7",
        "wp-coding-standards/wpcs": "^3.1"
    },
    "autoload": {
        "psr-4": {
            "CustomSitemapGenerator\\": "src/"
        }
    },
    "scripts": {
        "phpcs": "phpcs --standard=WordPress --extensions=php src/ plugin.php"
    }
}

Configure package.json to use wp-scripts for asset compilation. Add the following scripts:

{
    "name": "custom-sitemap-generator",
    "version": "1.0.0",
    "description": "Custom XML Sitemap Generator Gutenberg Block",
    "main": "build/index.js",
    "scripts": {
        "build": "wp-scripts build",
        "start": "wp-scripts start",
        "lint:css": "wp-scripts lint-style",
        "lint:js": "wp-scripts lint-js"
    },
    "keywords": ["wordpress", "gutenberg", "block", "vue", "sitemap"],
    "author": "Your Name",
    "license": "GPL-2.0-or-later",
    "dependencies": {
        "vue": "^3.2.45"
    },
    "devDependencies": {
        "@wordpress/scripts": "^25.4.0",
        "vue-loader": "^17.0.1",
        "vue-template-compiler": "^2.7.14"
    },
    "browserslist": {
        "production": [
            ">0.2%",
            "not dead",
            "not op_mini all"
        ],
        "development": [
            "last 1 chrome version",
            "last 1 firefox version",
            "last 1 safari version"
        ]
    }
}

Create a webpack.config.js in the root of your plugin directory to configure Vue Loader.

const { getWebpackConfig } = require( '@wordpress/scripts' );
const VueLoaderPlugin = require( 'vue-loader/lib/plugin' );

module.exports = getWebpackConfig( {
    entry: './src/index.js',
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader',
            },
            // you will need sass loader if you use scss files
            // {
            //  test: /\.scss$/,
            //  use: [
            //      'vue-style-loader',
            //      'css-loader',
            //      'sass-loader'
            //  ]
            // }
        ],
    },
    plugins: [
        // make sure to include the vloader plugin
        new VueLoaderPlugin(),
    ],
} );

Plugin Structure and Main File

Organize your plugin files as follows:

  • custom-sitemap-generator/
    • plugin.php (Main plugin file)
    • composer.json
    • package.json
    • webpack.config.js
    • src/
      • index.js (Gutenberg block registration)
      • block.json (Block metadata)
      • editor.scss
      • style.scss
      • components/
        • SitemapSettings.vue (Vue component for settings)
        • SitemapPreview.vue (Vue component for preview)
      • utils/
        • api.js (Helper for API calls)
    • build/ (Compiled assets will go here)

Create the main plugin file, plugin.php. This file will register the Gutenberg block and enqueue necessary assets.

<?php
/**
 * Plugin Name: Custom Sitemap Generator
 * Description: A Gutenberg block to generate and manage XML sitemaps.
 * Version: 1.0.0
 * Author: Your Name
 * License: GPL-2.0-or-later
 * Text Domain: custom-sitemap-generator
 *
 * @package CustomSitemapGenerator
 */

namespace CustomSitemapGenerator;

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

/**
 * Registers the block using the metadata loaded from the `block.json` file.
 * Behind the scenes, it registers also all assets so they can be enqueued
 * through the block editor in the corresponding context.
 *
 * @see https://developer.wordpress.org/reference/functions/register_block_type/
 */
function register_sitemap_block() {
    register_block_type( __DIR__ . '/build' );
}
add_action( 'init', __NAMESPACE__ . '\\register_sitemap_block' );

/**
 * Enqueue custom scripts for the sitemap generator.
 */
function enqueue_custom_sitemap_scripts() {
    // Enqueue Vue.js if not already loaded by WordPress core or other plugins.
    // This is a simplified check; a more robust solution might involve checking
    // registered script handles or using a CDN.
    if ( ! wp_script_is( 'vue-3', 'enqueued' ) ) {
        wp_enqueue_script(
            'vue-3',
            'https://unpkg.com/vue@3/dist/vue.global.js', // Using CDN for simplicity
            array(),
            '3.2.45',
            true
        );
    }

    // Enqueue our compiled block assets.
    $asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php' );

    wp_enqueue_script(
        'custom-sitemap-generator-block-editor',
        plugins_url( 'build/index.js', __FILE__ ),
        $asset_file['dependencies'],
        $asset_file['version']
    );

    wp_enqueue_style(
        'custom-sitemap-generator-block-editor',
        plugins_url( 'build/index.css', __FILE__ ),
        array( 'wp-edit-blocks' ),
        $asset_file['version']
    );

    // Localize script with data if needed, e.g., REST API nonce.
    wp_localize_script(
        'custom-sitemap-generator-block-editor',
        'customSitemapGeneratorData',
        array(
            'restUrl' => esc_url_raw( rest_url() ),
            'nonce'   => wp_create_nonce( 'wp_rest' ),
            // Add any other necessary data here
        )
    );
}
add_action( 'enqueue_block_editor_assets', __NAMESPACE__ . '\\enqueue_custom_sitemap_scripts' );

/**
 * Register REST API endpoint for sitemap generation.
 */
function register_sitemap_api_route() {
    register_rest_route( 'custom-sitemap-generator/v1', '/generate', array(
        'methods'             => \WP_REST_Server::CREATABLE,
        'callback'            => __NAMESPACE__ . '\\handle_sitemap_generation',
        'permission_callback' => function () {
            return current_user_can( 'manage_options' ); // Example permission
        },
    ) );
}
add_action( 'rest_api_init', __NAMESPACE__ . '\\register_sitemap_api_route' );

/**
 * Handles the sitemap generation request.
 *
 * @param \WP_REST_Request $request Full data about the request.
 * @return \WP_REST_Response|\WP_Error Response object on success, or \WP_Error object on failure.
 */
function handle_sitemap_generation( \WP_REST_Request $request ) {
    // In a real-world scenario, this would involve complex logic to fetch posts,
    // pages, custom post types, and generate the XML.
    // For this example, we'll simulate a successful generation.

    $posts_to_include = $request->get_param( 'posts' ) ?: array(); // Example parameter
    $pages_to_include = $request->get_param( 'pages' ) ?: array();

    // Simulate generating XML content
    $xml_content = '<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';

    // Example: Add homepage
    $xml_content .= '<url><loc>' . esc_url( home_url( '/' ) ) . '</loc></url>';

    // Example: Add posts (replace with actual query)
    $args = array(
        'post_type'      => 'post',
        'posts_per_page' => -1,
        'post_status'    => 'publish',
    );
    $query = new \WP_Query( $args );
    if ( $query->have_posts() ) {
        while ( $query->have_posts() ) {
            $query->the_post();
            $xml_content .= '<url><loc>' . esc_url( get_permalink() ) . '</loc></url>';
        }
        wp_reset_postdata();
    }

    $xml_content .= '</urlset>';

    // In a real plugin, you might save this to a file or serve it directly.
    // For this example, we'll just return it.
    return new \WP_REST_Response( array( 'xml' => $xml_content ), 200 );
}

// Add a filter to allow the block to be rendered server-side if needed
// add_filter( 'render_block', __NAMESPACE__ . '\\render_custom_sitemap_block', 10, 2 );
// function render_custom_sitemap_block( $block_content, $block ) {
//     if ( isset( $block['blockName'] ) && 'custom-sitemap-generator/sitemap-block' === $block['blockName'] ) {
//         // Logic to render the sitemap block server-side if necessary
//         // For this example, we're primarily focusing on the editor experience.
//     }
//     return $block_content;
// }

Gutenberg Block Registration and Metadata

The block.json file defines the metadata for your Gutenberg block. This includes its name, title, category, icon, and the scripts/styles to be enqueued.

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "custom-sitemap-generator/sitemap-block",
    "version": "0.1.0",
    "title": "XML Sitemap Generator",
    "category": "widgets",
    "icon": "admin-site",
    "description": "Generate and manage your XML sitemap.",
    "keywords": ["sitemap", "seo", "xml"],
    "attributes": {
        "includePosts": {
            "type": "boolean",
            "default": true
        },
        "includePages": {
            "type": "boolean",
            "default": true
        },
        "customUrls": {
            "type": "array",
            "default": []
        }
    },
    "supports": {
        "html": false
    },
    "textdomain": "custom-sitemap-generator",
    "editorScript": "file:./build/index.js",
    "editorStyle": "file:./build/index.css",
    "style": "file:./build/style-index.css"
}

The src/index.js file is the entry point for your block’s JavaScript. It registers the block and defines its editor interface.

/**
 * WordPress dependencies
 */
import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import './style.scss';
import './editor.scss';
import Edit from './components/Edit.vue'; // We will create this component

// Register the block
registerBlockType( 'custom-sitemap-generator/sitemap-block', {
    edit: Edit,
    save: () => null, // We will handle saving via REST API and not store complex data in post_content
} );

Vue.js Micro-frontend for the Editor Interface

We’ll create a Vue component, src/components/Edit.vue, which will serve as the editor interface for our block. This component will manage the block’s settings and interact with the WordPress REST API.

<template>
    <div class="custom-sitemap-generator-block">
        <div class="block-controls">
            <h3>{{ __('Sitemap Settings', 'custom-sitemap-generator') }}</h3>
            <ToggleControl
                label="Include Posts"
                checked={ includePosts }
                onChange={ ( value ) => updateAttribute( 'includePosts', value ) }
            />
            <ToggleControl
                label="Include Pages"
                checked={ includePages }
                onChange={ ( value ) => updateAttribute( 'includePages', value ) }
            />
            <!-- Add more controls for custom URLs, post types, etc. -->
            <Button isPrimary onClick={ generateSitemap }>
                { __('Generate Sitemap', 'custom-sitemap-generator') }
            </Button>
        </div>
        <div class="sitemap-preview" v-if="sitemapXml">
            <h3>{{ __('Generated Sitemap (XML)', 'custom-sitemap-generator') }}</h3>
            <pre>{{ sitemapXml }}</pre>
            <!-- Add options to download or copy the XML -->
        </div>
        <div v-if="isLoading">{{ __('Generating...', 'custom-sitemap-generator') }}</div>
        <div v-if="error" class="error-message">{{ error }}</div>
    </div>
</template>

<script>
import { ToggleControl, Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useSelect, useDispatch } from '@wordpress/data';
import { useState, useEffect } from 'vue';
import api from '../utils/api'; // Our API helper

export default {
    name: 'SitemapGeneratorEdit',
    components: {
        ToggleControl,
        Button,
    },
    setup( props ) {
        const { attributes, setAttributes } = props;

        const includePosts = useState( attributes.includePosts );
        const includePages = useState( attributes.includePages );
        const sitemapXml = useState( '' );
        const isLoading = useState( false );
        const error = useState( '' );

        // Update local state when attributes change
        useEffect( () => {
            includePosts.value = attributes.includePosts;
            includePages.value = attributes.includePages;
        }, [ attributes.includePosts, attributes.includePages ] );

        const updateAttribute = ( key, value ) => {
            setAttributes( { [key]: value } );
            // Optionally trigger generation or update preview here
        };

        const generateSitemap = async () => {
            isLoading.value = true;
            error.value = '';
            sitemapXml.value = '';

            try {
                const response = await api.post( 'custom-sitemap-generator/v1/generate', {
                    posts: includePosts.value,
                    pages: includePages.value,
                    // Add other parameters from attributes
                } );

                if ( response.data && response.data.xml ) {
                    sitemapXml.value = response.data.xml;
                } else {
                    throw new Error( __('Failed to generate sitemap. Unexpected response.', 'custom-sitemap-generator') );
                }
            } catch ( e ) {
                console.error( 'Sitemap generation error:', e );
                error.value = e.message || __('An unknown error occurred.', 'custom-sitemap-generator');
            } finally {
                isLoading.value = false;
            }
        };

        return {
            includePosts,
            includePages,
            sitemapXml,
            isLoading,
            error,
            updateAttribute,
            generateSitemap,
            __ // Make __ available in template
        };
    },
};
</script>

<style scoped>
.custom-sitemap-generator-block {
    border: 1px solid #ddd;
    padding: 15px;
    background-color: #f9f9f9;
}
.block-controls {
    margin-bottom: 20px;
}
.sitemap-preview {
    margin-top: 20px;
    background-color: #eee;
    padding: 10px;
    border: 1px solid #ccc;
    overflow-x: auto;
}
.sitemap-preview pre {
    white-space: pre-wrap;
    word-wrap: break-word;
    font-size: 0.9em;
}
.error-message {
    color: red;
    font-weight: bold;
}
</style>

We need to adapt the src/index.js to use this Vue component. Since Gutenberg’s `edit` prop expects a React component or a function returning JSX, we’ll use a wrapper or a library that bridges Vue and React/Gutenberg’s rendering context. A common approach is to use a simple wrapper that mounts the Vue app.

First, let’s create a Vue app instance and mount it. We’ll need to adjust src/index.js and potentially create a new entry point for the Vue app itself.

Revised src/index.js:

/**
 * WordPress dependencies
 */
import { registerBlockType } from '@wordpress/blocks';
import { createRoot } from 'react-dom/client'; // For React 18+
// import { render } from 'react-dom'; // For React 17 and below

/**
 * Internal dependencies
 */
import './style.scss';
import './editor.scss';
import VueBlockWrapper from './VueBlockWrapper'; // We will create this wrapper

// Register the block
registerBlockType( 'custom-sitemap-generator/sitemap-block', {
    edit: ( { attributes, setAttributes } ) => {
        // This component will render our Vue app
        return <VueBlockWrapper attributes={ attributes } setAttributes={ setAttributes } />;
    },
    save: () => null, // We handle saving via REST API
} );

Now, create the src/VueBlockWrapper.js file. This acts as a bridge, rendering a React component that in turn mounts and manages our Vue application.

import React, { useEffect, useRef } from 'react';
import { create } from 'vue';
import SitemapGeneratorEdit from './components/Edit.vue';

const VueBlockWrapper = ( { attributes, setAttributes } ) => {
    const vueAppContainer = useRef( null );
    const vueAppInstance = useRef( null );

    useEffect( () => {
        // Ensure the container exists
        if ( ! vueAppContainer.current ) {
            return;
        }

        // If an instance already exists, update its props and re-render
        if ( vueAppInstance.current ) {
            vueAppInstance.current.component.props.attributes = attributes;
            vueAppInstance.current.component.props.setAttributes = setAttributes;
            // Vue 3 doesn't have a direct re-render method like Vue 2's $forceUpdate.
            // We rely on reactivity. If props change, the component should update.
            // If complex updates are needed, consider unmounting and remounting.
            return;
        }

        // Create a new Vue app instance
        const App = {
            extends: SitemapGeneratorEdit, // Extend our Vue component
            props: ['attributes', 'setAttributes'], // Define props to receive from React
            // You might need to adjust how props are passed or accessed in Vue 3
            // For Vue 3, you might directly use the props in the template or methods
        };

        // Mount the Vue app
        vueAppInstance.current = create( App, {
            props: {
                attributes: attributes,
                setAttributes: setAttributes,
            },
        } );
        vueAppInstance.current.mount( vueAppContainer.current );

        // Cleanup on unmount
        return () => {
            if ( vueAppInstance.current ) {
                vueAppInstance.current.unmount();
                vueAppInstance.current = null;
            }
        };
    }, [ attributes, setAttributes ] ); // Re-run effect if attributes or setAttributes change

    // Render a div that Vue will mount to
    return React.createElement( 'div', { ref: vueAppContainer } );
};

export default VueBlockWrapper;

Note on Vue 3 and React Integration: The integration of Vue 3 within a React-based environment like Gutenberg can be complex. The above VueBlockWrapper.js uses a basic approach. For more robust solutions, consider libraries like @vue/reactivity or dedicated wrappers that handle prop synchronization and lifecycle management more effectively. The key is that the `edit` function in `index.js` must return a React element, and that element needs to manage the lifecycle of the Vue application.

API Interaction and Utilities

Create a utility file for handling API requests, src/utils/api.js. This will abstract the interaction with the WordPress REST API.

import axios from 'axios';

const api = axios.create( {
    baseURL: customSitemapGeneratorData.restUrl, // Provided by wp_localize_script
    headers: {
        'X-WP-Nonce': customSitemapGeneratorData.nonce, // Provided by wp_localize_script
    },
} );

export default {
    get( endpoint, params ) {
        return api.get( endpoint, { params } );
    },
    post( endpoint, data, params ) {
        return api.post( endpoint, data, { params } );
    },
    put( endpoint, data, params ) {
        return api.put( endpoint, data, { params } );
    },
    delete( endpoint, params ) {
        return api.delete( endpoint, { params } );
    },
};

Styling the Block

Add basic styles for the block editor and the frontend. Create src/editor.scss and src/style.scss.

/** editor.scss */
.custom-sitemap-generator-block {
    border: 1px dashed #9b9b9b;
    padding: 10px;
    background-color: #f0f0f0;
    text-align: center;
}

.custom-sitemap-generator-block h3 {
    margin-top: 0;
    font-size: 1.1em;
    color: #333;
}

/* Add styles for ToggleControl, Button etc. if needed */
.block-controls .components-base-control {
    margin-bottom: 10px;
}

.block-controls .components-button {
    margin-top: 15px;
}

.sitemap-preview pre {
    background-color: #fff;
    padding: 15px;
    border: 1px solid #ccc;
    overflow-x: auto;
    font-size: 0.85em;
    max-height: 300px;
}

.error-message {
    color: #dc3232;
    font-weight: bold;
    margin-top: 10px;
}
/** style.scss */
.custom-sitemap-generator-block {
    /* Styles for the block on the frontend if it were to render something directly */
    /* For this example, the block primarily acts as a control panel in the editor */
    /* and doesn't render content directly to the post_content. */
    /* If you wanted to display a link to the sitemap, you'd add it here. */
}

Run the build process to compile your assets:

npm run build

This will create the build/ directory with index.js, index.css, and style-index.css. The index.asset.php file will also be generated, containing dependencies and version information for enqueuing.

Testing and Refinements

Activate your “Custom Sitemap Generator” plugin in the WordPress admin area. Navigate to the post or page editor and add the “XML Sitemap Generator” block. You should see the Vue-based interface. Test the “Include Posts” and “Include Pages” toggles and click “Generate Sitemap”. If everything is configured correctly, you should see the generated XML output below.

Potential Refinements:

  • Sitemap Serving: Instead of just displaying the XML in the editor, implement logic to save the generated XML to a file (e.g., sitemap.xml in the WordPress root) or serve it via a dedicated REST API endpoint that returns the XML content type.
  • Advanced Options: Add controls for custom post types, taxonomies, date inclusion/exclusion, priority, change frequency, and custom URL entries.
  • Error Handling: Improve error messages and logging for failed generation attempts.
  • Security: Ensure proper nonce verification and capability checks for the REST API endpoint.
  • Performance: For large sites, optimize the post/page querying logic to avoid timeouts. Consider background processing for sitemap generation.
  • Vue 3 Reactivity: For more complex state management within the Vue component, explore Vue 3’s Composition API or Pinia for state management.
  • Accessibility: Ensure all controls and outputs are accessible.

By integrating Vue.js as a micro-frontend, you gain a powerful and maintainable way to build complex Gutenberg block interfaces, separating concerns and leveraging modern JavaScript tooling within the WordPress ecosystem.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Implementing automated compliance reporting for custom customer support tickets ledgers using FPDF customized scripts
  • Troubleshooting database connection pool timeouts in production when using modern Timber Twig templating engines wrappers
  • WordPress Development Recipe: Secure token-based API authentication for SendGrid transactional mailer in custom plugins
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency PayPal Checkout REST handlers
  • How to implement native Redis caching layers for high-volume custom taxonomy queries in Understrap styling structures

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (38)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (20)
  • WordPress Plugin Development (17)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Implementing automated compliance reporting for custom customer support tickets ledgers using FPDF customized scripts
  • Troubleshooting database connection pool timeouts in production when using modern Timber Twig templating engines wrappers
  • WordPress Development Recipe: Secure token-based API authentication for SendGrid transactional mailer in custom plugins

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala