Most rendering engines do need to handle multiple vertex structures depending on the type of shading being used on the mesh.
If there are only a small number of discrete vertex structures, you could simply create an enum to list them and define a struct for each one. However, many rendering engines take a fully data-driven approach to it, where there is no predefined vertex structure, but just a buffer of raw bytes plus a metadata description that lists the offset and type of each vertex component within the structure. It creates a lot of extra housekeeping code, but it is the most flexible way to deal with vertex structures.
An alternate approach is to use "deinterleaved" vertices, where each vertex component is in its own array. You'd have one array of positions, one array of colors, one array of normals, etc. This allows for components to be present or absent simply by populating that array or leaving it empty. The GPU can read deinterleaved vertex data as well as interleaved data; you just have to configure the vertex arrays appropriately. Deinterleaved vertices may be less efficient overall, as more memory transactions are required to read the data this way. On the other hand, that may not matter much if the GPU is not vertex-bound on your scene.
An advantage of the deinterleaved approach is that sometimes different vertex components are relevant for different rendering passes on the same mesh; for example, shadow rendering usually only needs positions and not colors. Deinterleaved vertex data makes it easy to supply only those components that are relevant for the current pass, so the GPU need not spend extra bandwidth reading irrelevant data.
A middle ground is to partially deinterleave the data, so that positions are in one array and all the other components are interleaved together in another array, for example (or any other combination you choose).